  OO tests for OO code
       Nice performance boost
       Easy-to-organize test suites




http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
  Plenty!




http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
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
lib/!
     | Person/!
     | `-- Employee.pm!
     `-- Person.pm!

     t/!
     | lib/!
     | | My/!
     | | | Test/!
     | | | `-- Class.pm!
     | | TestsFor/!
     | | | Person/!
     | | | `-- Employee.pm!
     | | `-- Person.pm!
     `-- test_class_tests.t!


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
prove -lv -It/lib !
       t/lib/TestsFor/Person.pm !

     # INIT { Test::Class->runtests }!




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
     Beginning Perl, Wrox Press
          Introduction to Perl
          CPAN
          Modules
          Objects (Moose!)
          Testing
          PSGI+Plack
          DBIx::Class
          Catalyst
          And more!


http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass

A Whirlwind Tour of Test::Class

  • 2.
      OO testsfor OO code   Nice performance boost   Easy-to-organize test suites http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass
  • 3.
  • 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
  • 6.
    lib/! | Person/! | `-- Employee.pm! `-- Person.pm! t/! | lib/! | | My/! | | | Test/! | | | `-- Class.pm! | | TestsFor/! | | | Person/! | | | `-- Employee.pm! | | `-- Person.pm! `-- test_class_tests.t! 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
  • 20.
    prove -lv -It/lib! t/lib/TestsFor/Person.pm ! # INIT { Test::Class->runtests }! 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 DoThis 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 yourtest 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
  • 45.
      Beginning Perl, Wrox Press   Introduction to Perl   CPAN   Modules   Objects (Moose!)   Testing   PSGI+Plack   DBIx::Class   Catalyst   And more! http://www.slideshare.net/Ovid/a-whirlwind-tour-of-testclass

Editor's Notes

  • #4 Single versus multiple processesTests that don’t map to classesDetailed testing strategiesWhy some of this presentation works