4. 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
5. use Test::Class::Most!
parent => 'My::Test::Class’;!
sub some_test : Tests { ... }!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
7. 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
8. 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
9. 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
10. 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
11. 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
12. 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
13. 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
14. 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
15. 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
16. 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
17. 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
18. 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
19. 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
21. 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
22. 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
23. $ 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
24. 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
25. 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
26. 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
27. 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
28. 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
29. 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
30. 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
31. 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
32. 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
33. 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
34. 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
35. 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
36. 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
37. 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
38. 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
39. # 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
40. # 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
41. # Do This!!
use Test::Class::Load ‘t/lib’;!
http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
42. 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
43. 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
44. 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