A Whirlwind Tour of Test::Class

45

Transcript of A Whirlwind Tour of Test::Class

Page 1: A Whirlwind Tour of Test::Class
Page 2: A Whirlwind Tour of Test::Class

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

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

Page 3: A Whirlwind Tour of Test::Class

  Plenty!

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

Page 4: A Whirlwind Tour of Test::Class

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

Page 5: A Whirlwind Tour of Test::Class

use Test::Class::Most! parent => 'My::Test::Class’;!

sub some_test : Tests { ... }!

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

Page 6: A Whirlwind Tour of Test::Class

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

Page 7: A Whirlwind Tour of Test::Class

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

Page 8: A Whirlwind Tour of Test::Class

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

Page 9: A Whirlwind Tour of Test::Class

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

Page 10: A Whirlwind Tour of Test::Class

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

Page 11: A Whirlwind Tour of Test::Class

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

Page 12: A Whirlwind Tour of Test::Class

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

Page 13: A Whirlwind Tour of Test::Class

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

Page 14: A Whirlwind Tour of Test::Class

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

Page 15: A Whirlwind Tour of Test::Class

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

Page 16: A Whirlwind Tour of Test::Class

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

Page 17: A Whirlwind Tour of Test::Class

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

Page 18: A Whirlwind Tour of Test::Class

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

Page 19: A Whirlwind Tour of Test::Class

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

Page 20: A Whirlwind Tour of Test::Class

prove -lv -It/lib \! t/lib/TestsFor/Person.pm !

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

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

Page 21: A Whirlwind Tour of Test::Class

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

Page 22: A Whirlwind Tour of Test::Class

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

Page 23: A Whirlwind Tour of Test::Class

$ 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

Page 24: A Whirlwind Tour of Test::Class

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

Page 25: A Whirlwind Tour of Test::Class

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

Page 26: A Whirlwind Tour of Test::Class

t/TestsFor/Person/Employee.pm .. # !# TestsFor::Person::Employee->constructor!

1..16!ok 1 - Person::Employee->can('new')!ok 2 - ... and the constructor should succeed!ok 3 - ... and the object it returns isa

Person::Employee!# !# TestsFor::Person::Employee->employee_number!ok 4 - Person::Employee-

>can('employee_number')!ok 5 - ... the employee number is correct!# !# TestsFor::Person::Employee->full_name!ok 6 - first_name correct!ok 7 - last_name correct!ok 8 - Person::Employee->can('full_name')!ok 9 - ... and it should return the correct

fullname!

# !# TestsFor::Person->constructor!ok 10 - Person->can('new')!ok 11 - ... and the constructor should

succeed!ok 12 - ... and the object it returns isa

Person!# !# TestsFor::Person->full_name!ok 13 - first_name correct!ok 14 - last_name correct!ok 15 - Person->can('full_name')!ok 16 - ... and it should return the correct

fullname!ok!All tests successful.!Files=1, Tests=16, 0 wallclock secs!Result: PASS!

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

Page 27: A Whirlwind Tour of Test::Class

t/TestsFor/Person/Employee.pm .. # !# TestsFor::Person::Employee->constructor!

1..16!ok 1 - Person::Employee->can('new')!ok 2 - ... and the constructor should succeed!ok 3 - ... and the object it returns isa

Person::Employee!# !# TestsFor::Person::Employee->employee_number!ok 4 - Person::Employee-

>can('employee_number')!ok 5 - ... the employee number is correct!# !# TestsFor::Person::Employee->full_name!ok 6 - first_name correct!ok 7 - last_name correct!ok 8 - Person::Employee->can('full_name')!ok 9 - ... and it should return the correct

fullname!

# !# TestsFor::Person->constructor!ok 10 - Person->can('new')!ok 11 - ... and the constructor should

succeed!ok 12 - ... and the object it returns isa

Person!# !# TestsFor::Person->full_name!ok 13 - first_name correct!ok 14 - last_name correct!ok 15 - Person->can('full_name')!ok 16 - ... and it should return the correct

fullname!ok!All tests successful.!Files=1, Tests=16, 0 wallclock secs!Result: PASS!

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

Page 28: A Whirlwind Tour of Test::Class

t/TestsFor/Person/Employee.pm .. # !# TestsFor::Person::Employee->constructor!

1..16!ok 1 - Person::Employee->can('new')!ok 2 - ... and the constructor should succeed!ok 3 - ... and the object it returns isa

Person::Employee!# !# TestsFor::Person::Employee->employee_number!ok 4 - Person::Employee-

>can('employee_number')!ok 5 - ... the employee number is correct!# !# TestsFor::Person::Employee->full_name!ok 6 - first_name correct!ok 7 - last_name correct!ok 8 - Person::Employee->can('full_name')!ok 9 - ... and it should return the correct

fullname!

# !# TestsFor::Person->constructor!ok 10 - Person->can('new')!ok 11 - ... and the constructor should

succeed!ok 12 - ... and the object it returns isa

Person!# !# TestsFor::Person->full_name!ok 13 - first_name correct!ok 14 - last_name correct!ok 15 - Person->can('full_name')!ok 16 - ... and it should return the correct

fullname!ok!All tests successful.!Files=1, Tests=16, 0 wallclock secs!Result: PASS!

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

Page 29: A Whirlwind Tour of Test::Class

t/TestsFor/Person/Employee.pm .. # !# TestsFor::Person::Employee->constructor!

1..16!ok 1 - Person::Employee->can('new')!ok 2 - ... and the constructor should succeed!ok 3 - ... and the object it returns isa

Person::Employee!# !# TestsFor::Person::Employee->employee_number!ok 4 - Person::Employee-

>can('employee_number')!ok 5 - ... the employee number is correct!# !# TestsFor::Person::Employee->full_name!ok 6 - first_name correct!ok 7 - last_name correct!ok 8 - Person::Employee->can('full_name')!ok 9 - ... and it should return the correct

fullname!

# !# TestsFor::Person->constructor!ok 10 - Person->can('new')!ok 11 - ... and the constructor should

succeed!ok 12 - ... and the object it returns isa

Person!# !# TestsFor::Person->full_name!ok 13 - first_name correct!ok 14 - last_name correct!ok 15 - Person->can('full_name')!ok 16 - ... and it should return the correct

fullname!ok!All tests successful.!Files=1, Tests=16, 0 wallclock secs!Result: PASS!

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

Page 30: A Whirlwind Tour of Test::Class

t/TestsFor/Person/Employee.pm .. # !# TestsFor::Person::Employee->constructor!

1..16!ok 1 - Person::Employee->can('new')!ok 2 - ... and the constructor should succeed!ok 3 - ... and the object it returns isa

Person::Employee!# !# TestsFor::Person::Employee->employee_number!ok 4 - Person::Employee-

>can('employee_number')!ok 5 - ... the employee number is correct!# !# TestsFor::Person::Employee->full_name!ok 6 - first_name correct!ok 7 - last_name correct!ok 8 - Person::Employee->can('full_name')!ok 9 - ... and it should return the correct

fullname!

# !# TestsFor::Person->constructor!ok 10 - Person->can('new')!ok 11 - ... and the constructor should

succeed!ok 12 - ... and the object it returns isa

Person!# !# TestsFor::Person->full_name!ok 13 - first_name correct!ok 14 - last_name correct!ok 15 - Person->can('full_name')!ok 16 - ... and it should return the correct

fullname!ok!All tests successful.!Files=1, Tests=16, 0 wallclock secs!Result: PASS!

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

Page 31: A Whirlwind Tour of Test::Class

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

Page 32: A Whirlwind Tour of Test::Class

  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

Page 33: A Whirlwind Tour of Test::Class

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

Page 34: A Whirlwind Tour of Test::Class

  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

Page 35: A Whirlwind Tour of Test::Class

package TestsFor::Customer;!

use Test::Class::Most! parent => ‘My::Test::Class’,! attributes => [‘dbh’];!

sub aaa_connect_to_db : Tests(setup) {! my $test = shift;! my $dbh = DBI->connect(…);! # or $test->{dbh} = $dbh;! $test->dbh($dbh);!}!

sub some_test : Tests {! my $test = shift;! my $dbh = $test->dbh;!…!

package TestsFor::Person::Employee;!

use Test::Class::Most! parent => ‘TestsFor::Customer’,! attributes => [‘employee’];!

sub bbb_employee : Tests(setup) {! my $test = shift;! my $employee = $test->dbh->…;! $test->employee($employee);!}!

sub kill_employee : Tests {! my $test = shift;! my $employee = $test->employee;! can_ok( $employee, ‘suffocate’ );!

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

Page 36: A Whirlwind Tour of Test::Class

package TestsFor::Customer;!

use Test::Class::Most! parent => ‘My::Test::Class’,! attributes => [‘dbh’];!

sub aaa_connect_to_db : Tests(setup) {! my $test = shift;! my $dbh = DBI->connect(…);! # or $test->{dbh} = $dbh;! $test->dbh($dbh);!}!

sub some_test : Tests {! my $test = shift;! my $dbh = $test->dbh;!…!

package TestsFor::Person::Employee;!

use Test::Class::Most! parent => ‘TestsFor::Customer’,! attributes => [‘employee’];!

sub bbb_employee : Tests(setup) {! my $test = shift;! my $employee = $test->dbh->…;! $test->employee($employee);!}!

sub kill_employee : Tests {! my $test = shift;! my $employee = $test->employee;! can_ok( $employee, ‘suffocate’ );!

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

Page 37: A Whirlwind Tour of Test::Class

package TestsFor::Customer;!

use Test::Class::Most! parent => ‘My::Test::Class’,! attributes => [‘dbh’];!

sub setup : Tests(setup) {! my $test = shift;! my $dbh = DBI->connect(…);! # or $test->{dbh} = $dbh;! $test->dbh($dbh);!}!

sub some_test : Tests {! my $test = shift;! my $dbh = $test->dbh;!…!

package TestsFor::Person::Employee;!

use Test::Class::Most! parent => ‘TestsFor::Customer’,! attributes => [‘employee’];!

sub setup : Tests(setup) {! my $test = shift;! $test->SUPER::setup;! my $employee = $test->dbh->…;! $test->employee($employee);!}!

sub kill_employee : Tests {! my $test = shift;! my $employee = $test->employee;! can_ok( $employee, ‘suffocate’ );!

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

Page 38: A Whirlwind Tour of Test::Class

Before Tests

sub startup:Test(startup) {! my $test = shift;! $test->SUPER::startup;! # finish your startup!}!

sub setup : Test(setup) {! my $test = shift;! $test->SUPER::setup;! # finish your setup!}!

After Tests

sub teardown:Test(teardown){! my $test = shift;! # finish your teardown! $test->SUPER::teardown;!}!

sub shutdown:Test(shutdown){! my $test = shift;! # finish your shutdown! $test->SUPER::shutdown;!}!

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

Page 39: A Whirlwind Tour of Test::Class

# 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

Page 40: A Whirlwind Tour of Test::Class

# 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

Page 41: A Whirlwind Tour of Test::Class

# Do This!!use Test::Class::Load ‘t/lib’;!

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

Page 42: A Whirlwind Tour of Test::Class

  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

Page 43: A Whirlwind Tour of Test::Class

  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

Page 44: A Whirlwind Tour of Test::Class

  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

Page 45: A Whirlwind Tour of Test::Class

  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