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

1 by gregor herrmann
Import upstream version 1.002
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