4
package Test::Roo::Cookbook;
5
# ABSTRACT: Test::Roo examples
6
our $VERSION = '1.002'; # VERSION
11
# vim: ts=4 sts=4 sw=4 et:
21
Test::Roo::Cookbook - Test::Roo examples
29
This file offers usage ideas and examples for L<Test::Roo>.
31
=for Pod::Coverage method_names_here
33
=head1 ORGANIZING TEST CLASSES AND ROLES
35
=head2 Self-contained test file
37
A single test file could be used for simple tests where you want to
38
use Moo attributes for fixtures that get used by test blocks.
40
Here is an example that requires a C<corpus> attribute, stores
41
lines from that file in the C<lines> attribute and makes it
42
available to all test blocks:
44
# examples/cookbook/single_file.t
48
use MooX::Types::MooseLike::Base qw/ArrayRef/;
53
isa => sub { -f shift },
64
return [ map { lc } path( $self->corpus )->lines ];
67
test 'sorted' => sub {
69
is_deeply( $self->lines, [ sort @{$self->lines} ], "alphabetized");
72
test 'a to z' => sub {
74
my %letters = map { substr($_,0,1) => 1 } @{ $self->lines };
75
is_deeply( [sort keys %letters], ["a" .. "z"], "all letters found" );
79
run_me( { corpus => "/usr/share/dict/words" } );
80
# ... test other corpuses ...
84
=head2 Standalone test class
86
You don't have to put the test class into the F<.t> file. It's just a class.
88
Here is the same corpus checking example as before, but now as a class:
90
# examples/cookbook/lib/CorpusCheck.pm
95
use MooX::Types::MooseLike::Base qw/ArrayRef/;
100
isa => sub { -f shift },
111
return [ map { lc } path( $self->corpus )->lines ];
114
test 'sorted' => sub {
116
is_deeply( $self->lines, [ sort @{$self->lines} ], "alphabetized");
119
test 'a to z' => sub {
121
my %letters = map { substr($_,0,1) => 1 } @{ $self->lines };
122
is_deeply( [sort keys %letters], ["a" .. "z"], "all letters found" );
127
Running it from a F<.t> file doesn't even need L<Test::Roo>:
129
# examples/cookbook/standalone.t
137
CorpusCheck->run_tests({ corpus => "/usr/share/dict/words" });
141
=head2 Standalone Test Roles
143
The real power of L<Test::Roo> is decomposing test behaviors into
144
roles that can be reused.
146
Imagine we want to test a file-finder module like L<Path::Iterator::Rule>.
147
We could put tests for it into a role, then run the tests from a file that composes
148
that role. For example, here would be the test file:
150
# examples/cookbook/test-pir.pl
160
iterator_class => 'Path::Iterator::Rule',
167
Then in the distribution for L<Path::Class::Rule>, the same role
168
could be tested with a test file like this:
170
# examples/cookbook/test-pcr.pl
180
iterator_class => 'Path::Class::Rule',
181
result_type => 'Path::Class::Entity',
187
What is the common role that they are consuming? It sets up a test
188
directory, creates files and runs tests:
190
# examples/cookbook/lib/IteratorTest.pm
192
package IteratorTest;
195
use MooX::Types::MooseLike::Base qw/:all/;
196
use Class::Load qw/load_class/;
199
has [qw/iterator_class result_type/] => (
222
isa => InstanceOf ['Path::Tiny']
231
sub _build_description { return shift->iterator_class }
235
my $dir = Path::Tiny->tempdir;
236
$dir->child($_)->touchpath for @{ $self->test_files };
240
sub _build_rule_object {
242
load_class( $self->iterator_class );
243
return $self->iterator_class->new;
246
sub test_result_type {
247
my ( $self, $file ) = @_;
248
if ( my $type = $self->result_type ) {
249
isa_ok( $file, $type, $file );
252
is( ref($file), '', "$file is string" );
256
test 'find files' => sub {
258
$self->clear_rule_object; # make sure have a new one each time
261
my $rule = $self->rule_object;
262
my @files = $rule->file->all( $self->tempdir, { relative => 1 } );
264
is_deeply( \@files, $self->test_files, "correct list of files" )
265
or diag explain \@files;
267
$self->test_result_type($_) for @files;
274
=head1 CREATING AND MANAGING FIXTURES
276
=head2 Skipping all tests
278
If you need to skip all tests in the F<.t> file because some prerequisite
279
isn't available or some fixture couldn't be built, use a C<BUILD> method and
280
call C<< plan skip_all => $reason >>.
282
use Class::Load qw/try_load_class/;
289
# ... something that might die if unavailable ...
295
try_load_class('Class::Name')
296
or plan skip_all => "Class::Name required to run these tests";
298
eval { $self->fixture }
299
or plan skip_all => "Couldn't build fixture";
302
=head2 Setting a test description
304
You can override C<_build_description> to create a test description based
305
on other attributes. For example, the C<IteratorTest> package earlier
308
has [qw/iterator_class result_type/] => (
314
sub _build_description { return shift->iterator_class }
316
The C<iterator_class> attribute is required and then the description
317
is set to it. Or, there could be a more verbose description:
319
sub _build_description {
320
my $name = shift->iterator_class;
321
return "Testing the $name class"
324
=head2 Requiring a builder
326
A test role can specify a lazy attribute and then require the
327
consuming class to provide a builder for it.
335
requires '_build_fixture';
337
In the consuming class:
339
sub _build_fixture { ... }
341
=head2 Clearing fixtures
343
If a fixture has a clearer method, it can be easily reset during testing.
344
This works really well with lazy attributes which get regenerated on demand.
351
test "some test" => sub {
353
$self->clear_fixture;
357
=head1 MODIFIERS FOR SETUP AND TEARDOWN
359
=head2 Setting up a fixture before testing
361
When you need to do some extra work to set up a fixture, you can put a
362
method modifier on the C<setup> method. In some cases, this is more
363
intuitive than doing all the work in an attribute builder.
365
Here is an example that creates an SQLite table before any tests are
366
run and cleans up afterwards:
368
# example/cookbook/sqlite.t
377
default => sub { Path::Tiny->tempdir },
382
default => sub { shift->tempdir->child('test.sqlite3') },
385
has dbh => ( is => 'lazy', );
390
"dbi:SQLite:dbname=" . $self->dbfile, { RaiseError => 1 }
394
before 'setup' => sub {
396
$self->dbh->do("CREATE TABLE f (f1, f2, f3)");
399
after 'teardown' => sub { shift->clear_tempdir };
401
test 'first' => sub {
403
my $dbh = $self->dbh;
404
my $sth = $dbh->prepare("INSERT INTO f(f1,f2,f3) VALUES (?,?,?)");
405
ok( $sth->execute( "one", "two", "three" ), "inserted data" );
407
my $got = $dbh->selectrow_arrayref("SELECT * FROM f");
408
is_deeply( $got, [qw/one two three/], "read data" );
414
=head2 Running tests during setup and teardown
416
You can run any tests you like during setup or teardown. The previous example
417
could have written the setup and teardown hooks like this:
419
before 'setup' => sub {
421
ok( ! -f $self->dbfile, "test database file not created" );
422
ok( $self->dbh->do("CREATE TABLE f (f1, f2, f3)"), "created table");
423
ok( -f $self->dbfile, "test database file exists" );
426
after 'teardown' => sub {
428
my $dir = $self->tempdir;
429
$self->clear_tempdir;
430
ok( ! -f $dir, "tempdir cleaned up");
433
=head1 MODIFIERS ON TESTS
435
=head2 Global modifiers with C<each_test>
437
Modifying C<each_test> triggers methods before or after B<every> test block
438
defined with the C<test> function. Because this affects all tests, whether
439
from the test class or composed from roles, it needs to be used thoughtfully.
441
Here is an example that ensures that every test block is run in its own
442
separate temporary directory.
444
# examples/cookbook/with_tempd.t
447
use File::pushd qw/tempd/;
452
isa => sub { shift->isa('File::pushd') },
456
# tempd changes directory until the object is destroyed
457
# and the fixture caches the object until cleared
458
sub _build_tempdir { return tempd() }
460
# building attribute will change to temp directory
461
before each_test => sub { shift->tempdir };
463
# clearing attribute will change to original directory
464
after each_test => sub { shift->clear_tempdir };
466
# do stuff in a temp directory
467
test 'first test' => sub {
469
is( $self->tempdir, getcwd(), "cwd is " . $self->tempdir );
473
# do stuff in a separate, fresh temp directory
474
test 'second test' => sub {
476
is( $self->tempdir, getcwd(), "cwd is " . $self->tempdir );
483
=head2 Individual test modifiers
485
If you want to have method modifiers on an individual test, put your
486
L<Test::More> tests in a method, add modifiers to that method, and use C<test>
489
# examples/cookbook/hookable_test.t
493
has counter => ( is => 'rw', default => sub { 0 } );
497
ok( $self->counter > 0, "counter is positive" );
500
before is_positive => sub { shift->counter( 1 ) };
502
test 'hookable' => sub { shift->is_positive };
507
=head2 Wrapping tests
509
As a middle ground between global and individual modifiers, if you need to call
510
some code repeatedly for some, but not all all tests, you can create a custom
511
test function. This might make sense for only a few tests, but could be
512
helpful if there are many that need similar behavior, but you can't make it
513
global by modifying C<each_test>.
515
The following example clears the fixture before tests defined with the
516
C<fresh_test> function.
518
# examples/cookbook/wrapped.t
530
sub _build_fixture { "Hello World" }
533
my ($name, $code) = @_;
536
$self->clear_fixture;
541
fresh_test 'first' => sub {
543
is ( $self->fixture, 'Hello World', "fixture has default" );
544
$self->fixture("Goodbye World");
547
fresh_test 'second' => sub {
549
is ( $self->fixture, 'Hello World', "fixture has default" );
557
David Golden <dagolden@cpan.org>
559
=head1 COPYRIGHT AND LICENSE
561
This software is Copyright (c) 2013 by David Golden.
563
This is free software, licensed under:
565
The Apache License, Version 2.0, January 2004