~ubuntu-branches/ubuntu/trusty/libtest-roo-perl/trusty-proposed

« back to all changes in this revision

Viewing changes to lib/Test/Roo/Cookbook.pm

  • Committer: Package Import Robot
  • Author(s): gregor herrmann
  • Date: 2014-01-14 19:25:11 UTC
  • Revision ID: package-import@ubuntu.com-20140114192511-9ycl4dc1zgamfnek
Tags: upstream-1.002
ImportĀ upstreamĀ versionĀ 1.002

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
use 5.008001;
 
2
use strictures;
 
3
 
 
4
package Test::Roo::Cookbook;
 
5
# ABSTRACT: Test::Roo examples
 
6
our $VERSION = '1.002'; # VERSION
 
7
 
 
8
1;
 
9
 
 
10
 
 
11
# vim: ts=4 sts=4 sw=4 et:
 
12
 
 
13
__END__
 
14
 
 
15
=pod
 
16
 
 
17
=encoding utf-8
 
18
 
 
19
=head1 NAME
 
20
 
 
21
Test::Roo::Cookbook - Test::Roo examples
 
22
 
 
23
=head1 VERSION
 
24
 
 
25
version 1.002
 
26
 
 
27
=head1 DESCRIPTION
 
28
 
 
29
This file offers usage ideas and examples for L<Test::Roo>.
 
30
 
 
31
=for Pod::Coverage method_names_here
 
32
 
 
33
=head1 ORGANIZING TEST CLASSES AND ROLES
 
34
 
 
35
=head2 Self-contained test file
 
36
 
 
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.
 
39
 
 
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:
 
43
 
 
44
    # examples/cookbook/single_file.t
 
45
 
 
46
    use Test::Roo;
 
47
 
 
48
    use MooX::Types::MooseLike::Base qw/ArrayRef/;
 
49
    use Path::Tiny;
 
50
 
 
51
    has corpus => (
 
52
        is       => 'ro',
 
53
        isa      => sub { -f shift },
 
54
        required => 1,
 
55
    );
 
56
 
 
57
    has lines => (
 
58
        is  => 'lazy',
 
59
        isa => ArrayRef,
 
60
    );
 
61
 
 
62
    sub _build_lines {
 
63
        my ($self) = @_;
 
64
        return [ map { lc } path( $self->corpus )->lines ];
 
65
    }
 
66
 
 
67
    test 'sorted' => sub {
 
68
        my $self = shift;
 
69
        is_deeply( $self->lines, [ sort @{$self->lines} ], "alphabetized");
 
70
    };
 
71
 
 
72
    test 'a to z' => sub {
 
73
        my $self = shift;
 
74
        my %letters = map { substr($_,0,1) => 1 } @{ $self->lines };
 
75
        is_deeply( [sort keys %letters], ["a" .. "z"], "all letters found" );
 
76
    };
 
77
 
 
78
 
 
79
    run_me( { corpus => "/usr/share/dict/words" } );
 
80
    # ... test other corpuses ...
 
81
 
 
82
    done_testing;
 
83
 
 
84
=head2 Standalone test class
 
85
 
 
86
You don't have to put the test class into the F<.t> file.  It's just a class.
 
87
 
 
88
Here is the same corpus checking example as before, but now as a class:
 
89
 
 
90
    # examples/cookbook/lib/CorpusCheck.pm
 
91
 
 
92
    package CorpusCheck;
 
93
    use Test::Roo;
 
94
 
 
95
    use MooX::Types::MooseLike::Base qw/ArrayRef/;
 
96
    use Path::Tiny;
 
97
 
 
98
    has corpus => (
 
99
        is       => 'ro',
 
100
        isa      => sub { -f shift },
 
101
        required => 1,
 
102
    );
 
103
 
 
104
    has lines => (
 
105
        is  => 'lazy',
 
106
        isa => ArrayRef,
 
107
    );
 
108
 
 
109
    sub _build_lines {
 
110
        my ($self) = @_;
 
111
        return [ map { lc } path( $self->corpus )->lines ];
 
112
    }
 
113
 
 
114
    test 'sorted' => sub {
 
115
        my $self = shift;
 
116
        is_deeply( $self->lines, [ sort @{$self->lines} ], "alphabetized");
 
117
    };
 
118
 
 
119
    test 'a to z' => sub {
 
120
        my $self = shift;
 
121
        my %letters = map { substr($_,0,1) => 1 } @{ $self->lines };
 
122
        is_deeply( [sort keys %letters], ["a" .. "z"], "all letters found" );
 
123
    };
 
124
 
 
125
    1;
 
126
 
 
127
Running it from a F<.t> file doesn't even need L<Test::Roo>:
 
128
 
 
129
    # examples/cookbook/standalone.t
 
130
 
 
131
    use strictures;
 
132
    use Test::More;
 
133
 
 
134
    use lib 'lib';
 
135
    use CorpusCheck;
 
136
 
 
137
    CorpusCheck->run_tests({ corpus => "/usr/share/dict/words" });
 
138
 
 
139
    done_testing;
 
140
 
 
141
=head2 Standalone Test Roles
 
142
 
 
143
The real power of L<Test::Roo> is decomposing test behaviors into
 
144
roles that can be reused.
 
145
 
 
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:
 
149
 
 
150
    # examples/cookbook/test-pir.pl
 
151
 
 
152
    use Test::Roo;
 
153
 
 
154
    use lib 'lib';
 
155
 
 
156
    with 'IteratorTest';
 
157
 
 
158
    run_me(
 
159
        {
 
160
            iterator_class => 'Path::Iterator::Rule',
 
161
            result_type    => '',
 
162
        }
 
163
    );
 
164
 
 
165
    done_testing;
 
166
 
 
167
Then in the distribution for L<Path::Class::Rule>, the same role
 
168
could be tested with a test file like this:
 
169
 
 
170
    # examples/cookbook/test-pcr.pl
 
171
 
 
172
    use Test::Roo;
 
173
 
 
174
    use lib 'lib';
 
175
 
 
176
    with 'IteratorTest';
 
177
 
 
178
    run_me(
 
179
        {
 
180
            iterator_class => 'Path::Class::Rule',
 
181
            result_type    => 'Path::Class::Entity',
 
182
        },
 
183
    );
 
184
 
 
185
    done_testing;
 
186
 
 
187
What is the common role that they are consuming?  It sets up a test
 
188
directory, creates files and runs tests:
 
189
 
 
190
    # examples/cookbook/lib/IteratorTest.pm
 
191
 
 
192
    package IteratorTest;
 
193
    use Test::Roo::Role;
 
194
 
 
195
    use MooX::Types::MooseLike::Base qw/:all/;
 
196
    use Class::Load qw/load_class/;
 
197
    use Path::Tiny;
 
198
 
 
199
    has [qw/iterator_class result_type/] => (
 
200
        is       => 'ro',
 
201
        isa      => Str,
 
202
        required => 1,
 
203
    );
 
204
 
 
205
    has test_files => (
 
206
        is      => 'ro',
 
207
        isa     => ArrayRef,
 
208
        default => sub {
 
209
            return [
 
210
                qw(
 
211
                aaaa
 
212
                bbbb
 
213
                cccc/dddd
 
214
                eeee/ffff/gggg
 
215
                )
 
216
            ];
 
217
        },
 
218
    );
 
219
 
 
220
    has tempdir => (
 
221
        is  => 'lazy',
 
222
        isa => InstanceOf ['Path::Tiny']
 
223
    );
 
224
 
 
225
    has rule_object => (
 
226
        is      => 'lazy',
 
227
        isa     => Object,
 
228
        clearer => 1,
 
229
    );
 
230
 
 
231
    sub _build_description { return shift->iterator_class }
 
232
 
 
233
    sub _build_tempdir {
 
234
        my ($self) = @_;
 
235
        my $dir = Path::Tiny->tempdir;
 
236
        $dir->child($_)->touchpath for @{ $self->test_files };
 
237
        return $dir;
 
238
    }
 
239
 
 
240
    sub _build_rule_object {
 
241
        my ($self) = @_;
 
242
        load_class( $self->iterator_class );
 
243
        return $self->iterator_class->new;
 
244
    }
 
245
 
 
246
    sub test_result_type {
 
247
        my ( $self, $file ) = @_;
 
248
        if ( my $type = $self->result_type ) {
 
249
            isa_ok( $file, $type, $file );
 
250
        }
 
251
        else {
 
252
            is( ref($file), '', "$file is string" );
 
253
        }
 
254
    }
 
255
 
 
256
    test 'find files' => sub {
 
257
        my $self = shift;
 
258
        $self->clear_rule_object; # make sure have a new one each time
 
259
 
 
260
        $self->tempdir;
 
261
        my $rule = $self->rule_object;
 
262
        my @files = $rule->file->all( $self->tempdir, { relative => 1 } );
 
263
 
 
264
        is_deeply( \@files, $self->test_files, "correct list of files" )
 
265
        or diag explain \@files;
 
266
 
 
267
        $self->test_result_type($_) for @files;
 
268
    };
 
269
 
 
270
    # ... more tests ...
 
271
 
 
272
    1;
 
273
 
 
274
=head1 CREATING AND MANAGING FIXTURES
 
275
 
 
276
=head2 Skipping all tests
 
277
 
 
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 >>.
 
281
 
 
282
    use Class::Load qw/try_load_class/;
 
283
 
 
284
    has fixture => (
 
285
        is => 'lazy',
 
286
    );
 
287
 
 
288
    sub _build_fixture {
 
289
        # ... something that might die if unavailable ...
 
290
    }
 
291
 
 
292
    sub BUILD {
 
293
        my ($self) = @_;
 
294
 
 
295
        try_load_class('Class::Name')
 
296
            or plan skip_all => "Class::Name required to run these tests";
 
297
 
 
298
        eval { $self->fixture }
 
299
            or plan skip_all => "Couldn't build fixture";
 
300
    }
 
301
 
 
302
=head2 Setting a test description
 
303
 
 
304
You can override C<_build_description> to create a test description based
 
305
on other attributes.  For example, the C<IteratorTest> package earlier
 
306
had these lines:
 
307
 
 
308
    has [qw/iterator_class result_type/] => (
 
309
        is       => 'ro',
 
310
        isa      => Str,
 
311
        required => 1,
 
312
    );
 
313
 
 
314
    sub _build_description { return shift->iterator_class }
 
315
 
 
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:
 
318
 
 
319
    sub _build_description {
 
320
        my $name = shift->iterator_class;
 
321
        return "Testing the $name class"
 
322
    }
 
323
 
 
324
=head2 Requiring a builder
 
325
 
 
326
A test role can specify a lazy attribute and then require the
 
327
consuming class to provide a builder for it.
 
328
 
 
329
In the test role:
 
330
 
 
331
    has fixture => (
 
332
        is => 'lazy',
 
333
    );
 
334
 
 
335
    requires '_build_fixture';
 
336
 
 
337
In the consuming class:
 
338
 
 
339
    sub _build_fixture { ... }
 
340
 
 
341
=head2 Clearing fixtures
 
342
 
 
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.
 
345
 
 
346
    has fixture => (
 
347
        is => 'lazy',
 
348
        clearer => 1,
 
349
    );
 
350
 
 
351
    test "some test" => sub {
 
352
        my $self = shift;
 
353
        $self->clear_fixture;
 
354
        ...
 
355
    };
 
356
 
 
357
=head1 MODIFIERS FOR SETUP AND TEARDOWN
 
358
 
 
359
=head2 Setting up a fixture before testing
 
360
 
 
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.
 
364
 
 
365
Here is an example that creates an SQLite table before any tests are
 
366
run and cleans up afterwards:
 
367
 
 
368
    # example/cookbook/sqlite.t
 
369
 
 
370
    use Test::Roo;
 
371
    use DBI;
 
372
    use Path::Tiny;
 
373
 
 
374
    has tempdir => (
 
375
        is      => 'ro',
 
376
        clearer => 1,
 
377
        default => sub { Path::Tiny->tempdir },
 
378
    );
 
379
 
 
380
    has dbfile => (
 
381
        is      => 'lazy',
 
382
        default => sub { shift->tempdir->child('test.sqlite3') },
 
383
    );
 
384
 
 
385
    has dbh => ( is => 'lazy', );
 
386
 
 
387
    sub _build_dbh {
 
388
        my $self = shift;
 
389
        DBI->connect(
 
390
            "dbi:SQLite:dbname=" . $self->dbfile, { RaiseError => 1 }
 
391
        );
 
392
    }
 
393
 
 
394
    before 'setup' => sub {
 
395
        my $self = shift;
 
396
        $self->dbh->do("CREATE TABLE f (f1, f2, f3)");
 
397
    };
 
398
 
 
399
    after 'teardown' => sub { shift->clear_tempdir };
 
400
 
 
401
    test 'first' => sub {
 
402
        my $self = shift;
 
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" );
 
406
 
 
407
        my $got = $dbh->selectrow_arrayref("SELECT * FROM f");
 
408
        is_deeply( $got, [qw/one two three/], "read data" );
 
409
    };
 
410
 
 
411
    run_me;
 
412
    done_testing;
 
413
 
 
414
=head2 Running tests during setup and teardown
 
415
 
 
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:
 
418
 
 
419
    before 'setup' => sub {
 
420
        my $self = shift;
 
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" );
 
424
    };
 
425
 
 
426
    after 'teardown' => sub {
 
427
        my $self = shift;
 
428
        my $dir = $self->tempdir;
 
429
        $self->clear_tempdir;
 
430
        ok( ! -f $dir, "tempdir cleaned up");
 
431
    };
 
432
 
 
433
=head1 MODIFIERS ON TESTS
 
434
 
 
435
=head2 Global modifiers with C<each_test>
 
436
 
 
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.
 
440
 
 
441
Here is an example that ensures that every test block is run in its own
 
442
separate temporary directory.
 
443
 
 
444
    # examples/cookbook/with_tempd.t
 
445
 
 
446
    use Test::Roo;
 
447
    use File::pushd qw/tempd/;
 
448
    use Cwd qw/getcwd/;
 
449
 
 
450
    has tempdir => (
 
451
        is => 'lazy',
 
452
        isa => sub { shift->isa('File::pushd') },
 
453
        clearer => 1,
 
454
    );
 
455
 
 
456
    # tempd changes directory until the object is destroyed
 
457
    # and the fixture caches the object until cleared
 
458
    sub _build_tempdir { return tempd() }
 
459
 
 
460
    # building attribute will change to temp directory
 
461
    before each_test => sub { shift->tempdir };
 
462
 
 
463
    # clearing attribute will change to original directory
 
464
    after each_test => sub { shift->clear_tempdir };
 
465
 
 
466
    # do stuff in a temp directory
 
467
    test 'first test' => sub {
 
468
        my $self = shift;
 
469
        is( $self->tempdir, getcwd(), "cwd is " . $self->tempdir );
 
470
        # ... more tests ...
 
471
    };
 
472
 
 
473
    # do stuff in a separate, fresh temp directory
 
474
    test 'second test' => sub {
 
475
        my $self = shift;
 
476
        is( $self->tempdir, getcwd(), "cwd is " . $self->tempdir );
 
477
        # ... more tests ...
 
478
    };
 
479
 
 
480
    run_me;
 
481
    done_testing;
 
482
 
 
483
=head2 Individual test modifiers
 
484
 
 
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>
 
487
to invoke it.
 
488
 
 
489
    # examples/cookbook/hookable_test.t
 
490
 
 
491
    use Test::Roo;
 
492
 
 
493
    has counter => ( is => 'rw', default => sub { 0 } );
 
494
 
 
495
    sub is_positive {
 
496
        my $self = shift;
 
497
        ok( $self->counter > 0, "counter is positive" );
 
498
    }
 
499
 
 
500
    before is_positive => sub { shift->counter( 1 ) };
 
501
 
 
502
    test 'hookable' => sub { shift->is_positive };
 
503
 
 
504
    run_me;
 
505
    done_testing;
 
506
 
 
507
=head2 Wrapping tests
 
508
 
 
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>.
 
514
 
 
515
The following example clears the fixture before tests defined with the
 
516
C<fresh_test> function.
 
517
 
 
518
    # examples/cookbook/wrapped.t
 
519
 
 
520
    use strict;
 
521
    use Test::Roo;
 
522
 
 
523
    has fixture => (
 
524
        is => 'rw',
 
525
        lazy => 1,
 
526
        builder => 1,
 
527
        clearer => 1,
 
528
    );
 
529
 
 
530
    sub _build_fixture { "Hello World" }
 
531
 
 
532
    sub fresh_test {
 
533
        my ($name, $code) = @_;
 
534
        test $name, sub {
 
535
            my $self = shift;
 
536
            $self->clear_fixture;
 
537
            $code->($self);
 
538
        };
 
539
    }
 
540
 
 
541
    fresh_test 'first' => sub {
 
542
        my $self = shift;
 
543
        is ( $self->fixture, 'Hello World', "fixture has default" );
 
544
        $self->fixture("Goodbye World");
 
545
    };
 
546
 
 
547
    fresh_test 'second' => sub {
 
548
        my $self = shift;
 
549
        is ( $self->fixture, 'Hello World', "fixture has default" );
 
550
    };
 
551
 
 
552
    run_me;
 
553
    done_testing;
 
554
 
 
555
=head1 AUTHOR
 
556
 
 
557
David Golden <dagolden@cpan.org>
 
558
 
 
559
=head1 COPYRIGHT AND LICENSE
 
560
 
 
561
This software is Copyright (c) 2013 by David Golden.
 
562
 
 
563
This is free software, licensed under:
 
564
 
 
565
  The Apache License, Version 2.0, January 2004
 
566
 
 
567
=cut