A Whirlwind Tour of Test::Class

Post on 10-May-2015

12.484 views 2 download

Tags:

Transcript of 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

  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!

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

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

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

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

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

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;!

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

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

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

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

# 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