use strict;!
use warnings;!
use Test::Exception 0.88;!
use Test::Differences 0.500;!
use Test::Deep 0.106;!
use Test::Warn 0.11;!
use Test::More 0.88;!
use parent 'My::Test::Class’;!
sub some_test : Tests { ... }!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
use Test::Class::Most!
parent => 'My::Test::Class’;!
sub some_test : Tests { ... }!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package My::Test::Class;!
use Test::Class::Most !
attributes => ['class'];!
INIT { Test::Class->runtests }!
sub startup : Tests(startup) {…}!
sub setup : Tests(setup) {}!
sub teardown : Tests(teardown) {}!
sub shutdown : Tests(shutdown) {}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package My::Test::Class;!
use Test::Class::Most !
attributes => ['class'];!
INIT { Test::Class->runtests }!
sub startup : Tests(startup) {…}!
sub setup : Tests(setup) {}!
sub teardown : Tests(teardown) {}!
sub shutdown : Tests(shutdown) {}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package My::Test::Class;!
use Test::Class::Most !
attributes => ['class'];!
INIT { Test::Class->runtests }!
sub startup : Tests(startup) {…}!
sub setup : Tests(setup) {}!
sub teardown : Tests(teardown) {}!
sub shutdown : Tests(shutdown) {}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package My::Test::Class;!
use Test::Class::Most !
attributes => ['class'];!
INIT { Test::Class->runtests }!
sub startup : Tests(startup) {…}!
sub setup : Tests(setup) {}!
sub teardown : Tests(teardown) {}!
sub shutdown : Tests(shutdown) {}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
sub startup : Tests(startup) {!
my $test = shift;!
return if $test->class;!
my $class = ref $test;!
$class =~ s/^TestsFor:://;!
eval "use $class";!
die $@ if $@;!
$test->class($class);!
}!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package Person;
use Moose;!
has ‘first_name’ => ( is => 'ro', isa => 'Str' );!
has ‘last_name’ => ( is => 'ro', isa => 'Str' );!
sub full_name {!
my $self = shift;!
return $self->first_name . ' ' !
. $self->last_name;!
}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package TestsFor::Person;!
use Test::Class::Most parent =>'My::Test::Class';!
sub constructor : Tests(3) {!
my $test = shift;!
my $class = $test->class;!
can_ok $class, 'new';
ok my $person = $class->new, !
'... and the constructor should succeed';!
isa_ok $person, $class, !
'... and the object it returns';!
}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package TestsFor::Person;!
use Test::Class::Most parent =>'My::Test::Class';!
sub constructor : Tests(3) {!
my $test = shift;!
my $class = $test->class;!
can_ok $class, 'new';
ok my $person = $class->new, !
'... and the constructor should succeed';!
isa_ok $person, $class, !
'... and the object it returns';!
}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package TestsFor::Person;!
use Test::Class::Most parent =>'My::Test::Class';!
sub constructor : Tests(3) {!
my $test = shift;!
my $class = $test->class;!
can_ok $class, 'new';
ok my $person = $class->new, !
'... and the constructor should succeed';!
isa_ok $person, $class, !
'... and the object it returns';!
}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package TestsFor::Person;!
use Test::Class::Most parent =>'My::Test::Class';!
sub constructor : Tests(3) {!
my $test = shift;!
my $class = $test->class;!
can_ok $class, 'new';
ok my $person = $class->new, !
'... and the constructor should succeed';!
isa_ok $person, $class, !
'... and the object it returns';!
}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package TestsFor::Person;!
use Test::Class::Most parent =>'My::Test::Class';!
sub constructor : Tests(3) {!
my $test = shift;!
my $class = $test->class;!
can_ok $class, 'new';
ok my $person = $class->new, !
'... and the constructor should succeed';!
isa_ok $person, $class, !
'... and the object it returns';!
}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package TestsFor::Person;!
use Test::Class::Most parent =>'My::Test::Class';!
sub constructor : Tests(3) {!
my $test = shift;!
my $class = $test->class;!
can_ok $class, 'new';
ok my $person = $class->new, !
'... and the constructor should succeed';!
isa_ok $person, $class, !
'... and the object it returns';!
}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package TestsFor::Person;!
use Test::Class::Most parent =>'My::Test::Class';!
sub constructor : Tests(3) {!
my $test = shift;!
my $class = $test->class;!
can_ok $class, 'new';
ok my $person = $class->new, !
'... and the constructor should succeed';!
isa_ok $person, $class, !
'... and the object it returns';!
}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
t/TestsFor/Person.pm .. # !
# TestsFor::Person->constructor!
1..3!
ok 1 - Person->can('new')!
ok 2 - ... and the constructor should succeed!
ok 3 - ... and the object it returns isa
Person!
ok!
All tests successful.!
Files=1, Tests=3, 0 wallclock secs!
Result: PASS!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package TestsFor::Person;!
Use Test::Class::Most parent => ‘My::Test::Class’;!
sub constructor : Tests(3) { … }!
sub full_name : Tests(4) {
my $test = shift;!
my $person = $test->class->new(!
first_name => 'Adrian',!
last_name => 'Howard',!
);!
is $person->first_name, 'Adrian', 'first_name correct';!
is $person->last_name, 'Howard', 'last_name correct';!
can_ok $person, 'full_name';!
is $person->full_name, 'Adrian Howard',!
'... and it should return the correct fullname';!
}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
$ prove –vl -It/lib t/TestsFor/Person.pm!
t/TestsFor/Person.pm .. # !
# TestsFor::Person->constructor!
1..7!
ok 1 - Person->can('new’)!
ok 2 - ... and the constructor should succeed!
ok 3 - ... and the object it returns isa Person!
# !
# TestsFor::Person->full_name!
ok 4 - first_name correct!
ok 5 - last_name correct!
ok 6 - Person->can('full_name')!
ok 7 - ... and it should return the correct fullname!
ok!
All tests successful.!
Files=1, Tests=7, 0 wallclock secs!
Result: PASS!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package Person::Employee;
use Moose;!
extends 'Person';!
has ‘employee_number’ => ( !
is => ‘ro’, !
isa => ‘Int’,!
);!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package TestsFor::Person::Employee;!
use Test::Class::Most parent => 'TestsFor::Person';!
sub employee_number : Tests(2) {
my $test = shift;!
my $employee = $test->class->new(!
first_name => 'John',!
last_name => 'Public',!
employee_number => 17,!
);!
can_ok $employee, 'employee_number’;!
is $employee->employee_number, 17, !
'... the employee number is correct';!
}!
1;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
t/TestsFor/Person/Employee.pm .. # ! # !
# TestsFor::Person::Employee->constructor! # TestsFor::Person->constructor!
ok 10 - Person->can('new')!
1..16! ok 11 - ... and the constructor should
ok 1 - Person::Employee->can('new')! succeed!
ok 2 - ... and the constructor should succeed! ok 12 - ... and the object it returns isa
ok 3 - ... and the object it returns isa Person!
Person::Employee! # !
# ! # TestsFor::Person->full_name!
# TestsFor::Person::Employee->employee_number! ok 13 - first_name correct!
ok 4 - Person::Employee- ok 14 - last_name correct!
>can('employee_number')! ok 15 - Person->can('full_name')!
ok 5 - ... the employee number is correct! ok 16 - ... and it should return the correct
# ! fullname!
# TestsFor::Person::Employee->full_name! ok!
ok 6 - first_name correct! All tests successful.!
ok 7 - last_name correct! Files=1, Tests=16, 0 wallclock secs!
ok 8 - Person::Employee->can('full_name')! Result: PASS!
ok 9 - ... and it should return the correct
fullname!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
t/TestsFor/Person/Employee.pm .. # ! # !
# TestsFor::Person::Employee->constructor! # TestsFor::Person->constructor!
ok 10 - Person->can('new')!
1..16! ok 11 - ... and the constructor should
ok 1 - Person::Employee->can('new')! succeed!
ok 2 - ... and the constructor should succeed! ok 12 - ... and the object it returns isa
ok 3 - ... and the object it returns isa Person!
Person::Employee! # !
# ! # TestsFor::Person->full_name!
# TestsFor::Person::Employee->employee_number! ok 13 - first_name correct!
ok 4 - Person::Employee- ok 14 - last_name correct!
>can('employee_number')! ok 15 - Person->can('full_name')!
ok 5 - ... the employee number is correct! ok 16 - ... and it should return the correct
# ! fullname!
# TestsFor::Person::Employee->full_name! ok!
ok 6 - first_name correct! All tests successful.!
ok 7 - last_name correct! Files=1, Tests=16, 0 wallclock secs!
ok 8 - Person::Employee->can('full_name')! Result: PASS!
ok 9 - ... and it should return the correct
fullname!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
t/TestsFor/Person/Employee.pm .. # ! # !
# TestsFor::Person::Employee->constructor! # TestsFor::Person->constructor!
ok 10 - Person->can('new')!
1..16! ok 11 - ... and the constructor should
ok 1 - Person::Employee->can('new')! succeed!
ok 2 - ... and the constructor should succeed! ok 12 - ... and the object it returns isa
ok 3 - ... and the object it returns isa Person!
Person::Employee! # !
# ! # TestsFor::Person->full_name!
# TestsFor::Person::Employee->employee_number! ok 13 - first_name correct!
ok 4 - Person::Employee- ok 14 - last_name correct!
>can('employee_number')! ok 15 - Person->can('full_name')!
ok 5 - ... the employee number is correct! ok 16 - ... and it should return the correct
# ! fullname!
# TestsFor::Person::Employee->full_name! ok!
ok 6 - first_name correct! All tests successful.!
ok 7 - last_name correct! Files=1, Tests=16, 0 wallclock secs!
ok 8 - Person::Employee->can('full_name')! Result: PASS!
ok 9 - ... and it should return the correct
fullname!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
t/TestsFor/Person/Employee.pm .. # ! # !
# TestsFor::Person::Employee->constructor! # TestsFor::Person->constructor!
ok 10 - Person->can('new')!
1..16! ok 11 - ... and the constructor should
ok 1 - Person::Employee->can('new')! succeed!
ok 2 - ... and the constructor should succeed! ok 12 - ... and the object it returns isa
ok 3 - ... and the object it returns isa Person!
Person::Employee! # !
# ! # TestsFor::Person->full_name!
# TestsFor::Person::Employee->employee_number! ok 13 - first_name correct!
ok 4 - Person::Employee- ok 14 - last_name correct!
>can('employee_number')! ok 15 - Person->can('full_name')!
ok 5 - ... the employee number is correct! ok 16 - ... and it should return the correct
# ! fullname!
# TestsFor::Person::Employee->full_name! ok!
ok 6 - first_name correct! All tests successful.!
ok 7 - last_name correct! Files=1, Tests=16, 0 wallclock secs!
ok 8 - Person::Employee->can('full_name')! Result: PASS!
ok 9 - ... and it should return the correct
fullname!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
t/TestsFor/Person/Employee.pm .. # ! # !
# TestsFor::Person::Employee->constructor! # TestsFor::Person->constructor!
ok 10 - Person->can('new')!
1..16! ok 11 - ... and the constructor should
ok 1 - Person::Employee->can('new')! succeed!
ok 2 - ... and the constructor should succeed! ok 12 - ... and the object it returns isa
ok 3 - ... and the object it returns isa Person!
Person::Employee! # !
# ! # TestsFor::Person->full_name!
# TestsFor::Person::Employee->employee_number! ok 13 - first_name correct!
ok 4 - Person::Employee- ok 14 - last_name correct!
>can('employee_number')! ok 15 - Person->can('full_name')!
ok 5 - ... the employee number is correct! ok 16 - ... and it should return the correct
# ! fullname!
# TestsFor::Person::Employee->full_name! ok!
ok 6 - first_name correct! All tests successful.!
ok 7 - last_name correct! Files=1, Tests=16, 0 wallclock secs!
ok 8 - Person::Employee->can('full_name')! Result: PASS!
ok 9 - ... and it should return the correct
fullname!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
sub startup : Tests(startup) {!
my $test = shift;!
return if $test->class;!
my $class = ref $test;!
$class =~ s/^TestsFor:://;!
eval "use $class";!
die $@ if $@;!
$test->class($class);!
}!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
TestsFor::Person::Employee inherits
TestsFor::Person’s tests
We don’t hard-code the class name
Inherited test methods rock!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
Attribute Name Phase
Tests(startup)! When the test class starts
Tests(setup)! Before each test method
Tests(teardown)! After each test method
Tests(shutdown)! When the test class finishes
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
Connect to a database (often in startup)
Set up fixtures/transactions (setup)
Clean fixtures/rollbacks (teardown)
Disconnect from database (shutdown)
(They’re a better place for object construction)
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package TestsFor::Customer;! package TestsFor::Person::Employee;!
use Test::Class::Most! use Test::Class::Most!
parent => ‘My::Test::Class’,! parent => ‘TestsFor::Customer’,!
attributes => [‘dbh’];! attributes => [‘employee’];!
sub aaa_connect_to_db : Tests(setup) {! sub bbb_employee : Tests(setup) {!
my $test = shift;! my $test = shift;!
my $dbh = DBI->connect(…);! my $employee = $test->dbh->…;!
# or $test->{dbh} = $dbh;! $test->employee($employee);!
$test->dbh($dbh);! }!
}!
sub kill_employee : Tests {!
sub some_test : Tests {! my $test = shift;!
my $test = shift;! my $employee = $test->employee;!
my $dbh = $test->dbh;! can_ok( $employee, ‘suffocate’ );!
…!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package TestsFor::Customer;! package TestsFor::Person::Employee;!
use Test::Class::Most! use Test::Class::Most!
parent => ‘My::Test::Class’,! parent => ‘TestsFor::Customer’,!
attributes => [‘dbh’];! attributes => [‘employee’];!
sub aaa_connect_to_db : Tests(setup) {! sub bbb_employee : Tests(setup) {!
my $test = shift;! my $test = shift;!
my $dbh = DBI->connect(…);! my $employee = $test->dbh->…;!
# or $test->{dbh} = $dbh;! $test->employee($employee);!
$test->dbh($dbh);! }!
}!
sub kill_employee : Tests {!
sub some_test : Tests {! my $test = shift;!
my $test = shift;! my $employee = $test->employee;!
my $dbh = $test->dbh;! can_ok( $employee, ‘suffocate’ );!
…!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
package TestsFor::Customer;! package TestsFor::Person::Employee;!
use Test::Class::Most! use Test::Class::Most!
parent => ‘My::Test::Class’,! parent => ‘TestsFor::Customer’,!
attributes => [‘dbh’];! attributes => [‘employee’];!
sub setup : Tests(setup) {! sub setup : Tests(setup) {!
my $test = shift;! my $test = shift;!
my $dbh = DBI->connect(…);! $test->SUPER::setup;!
# or $test->{dbh} = $dbh;! my $employee = $test->dbh->…;!
$test->dbh($dbh);! $test->employee($employee);!
}! }!
sub some_test : Tests {! sub kill_employee : Tests {!
my $test = shift;! my $test = shift;!
my $dbh = $test->dbh;! my $employee = $test->employee;!
…! can_ok( $employee, ‘suffocate’ );!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
Before Tests After Tests
sub startup:Test(startup) {! sub teardown:Test(teardown){!
my $test = shift;! my $test = shift;!
$test->SUPER::startup;! # finish your teardown!
# finish your startup! $test->SUPER::teardown;!
}! }!
sub setup : Test(setup) {! sub shutdown:Test(shutdown){!
my $test = shift;! my $test = shift;!
$test->SUPER::setup;! # finish your shutdown!
# finish your setup! $test->SUPER::shutdown;!
}! }!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
# t/test_class_tests.t!
# Don’t Do This!!
use lib ‘t/lib’;!
use TestsFor::Person;!
use TestsFor::Person::Employee;!
Test::Class->runtests;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
# Don’t Do This because you’ll forget one!!
use lib ‘t/lib’;!
use TestsFor::Person;!
use TestsFor::Person::Employee;!
use TestsFor::Database;!
use TestsFor::PurchaseOrder;!
use TestsFor::Alibi;!
use TestsFor::Embezzlement;!
use TestsFor::PaddingMySlides;!
Test::Class->runtests;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
# Do This!!
use Test::Class::Load ‘t/lib’;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
Finds your test classes for you
My example relies on
INIT { Test::Class->runtests }
in My::Test::Class.
Run the test suite:
prove -l t/test_class_tests.t!
Or a single test class:
prove -lv -It/lib t/TestsFor/Person.pm
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
Name test classes after their classes (if possible)
Name test methods after their methods (if possible)
… you can’t name a method new()!
Create your own base test class
… with stub test control methods
Name test control methods after their attribute
Don’t put tests in your test control methods
Do not hardcode the name of the class to test!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
Part 1: Getting started with Test::Class
Part 2: Inheriting Test::Class tests
Part 3: Making your testing life easier
Part 4: Using test control methods
Part 5: Test::Class Tricks
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass