1Test::Routine::Manual::UDseemro(C3o)ntributed Perl DocumTeenstta:t:iRoonutine::Manual::Demo(3)
2
3
4

NAME

6       Test::Routine::Manual::Demo - a walkthrough, in code, of Test::Routine
7

VERSION

9       version 0.027
10

The Demo

12   t/demo/01-demo.t
13         #!/bin/env perl
14         use strict;
15         use warnings;
16
17         # This test is both a test and an example of how Test::Routine works!  Welcome
18         # to t/01-demo.t, I will be your guide, rjbs.
19
20         {
21           # This block defines the HashTester package.  It's a Test::Routine, meaning
22           # it's a role.  We define state that the test will need to keep and any
23           # requirements we might have.
24           #
25           # Before we can run this test, we'll need to compose the role into a class so
26           # that we can make an instance.
27           package HashTester;
28           use Test::Routine;
29
30           # We import stuff from Test::More because, well, who wants to re-write all
31           # those really useful test routines that exist out there?  Maybe somebody,
32           # but not me.
33           use Test::More;
34
35           # ...but then we use namespace::autoclean to get rid of the routines once
36           # we've bound to them.  This is just standard Moose practice, anyway, right?
37           use namespace::autoclean;
38
39           # Finally, some state!  Every test will get called as method on an instance,
40           # and it will have this attribute.  Here are some points of interest:
41           #
42           # - We're giving this attribute a builder, so it will try to get built with a
43           #   call to $self->build_hash_to_test -- so each class that composes this
44           #   role can provide means for these attributes (fixtures) to be generated as
45           #   needed.
46           #
47           # - We are not adding "requires 'build_hash_to_test'", because then we can
48           #   apply this role to Moose::Object and instantiate it with a given value
49           #   in the constructor.  There will be an example of this below.  This lets
50           #   us re-use these tests in many variations without having to write class
51           #   after class.
52           #
53           # - We don't use lazy_build because it would create a clearer.  If someone
54           #   then cleared our lazy_build fixture, it could not be re-built in the
55           #   event that we'd gotten it explicitly from the constructor!
56           #
57           # Using Moose attributes for our state and fixtures allows us to get all of
58           # their powerful behaviors like types, delegation, traits, and so on, and
59           # allows us to decompose shared behavior into roles.
60           #
61           has hash_to_test => (
62             is  => 'ro',
63             isa => 'HashRef',
64             builder => 'build_hash_to_test',
65           );
66
67           # Here, we're just declaring an actual test that we will run.  This sub will
68           # get installed as a method with a name that won't get clobbered easily.  The
69           # method will be found later by run_tests so we can find and execute all
70           # tests on an instance.
71           #
72           # There is nothing magical about this method!  Calling this method is
73           # performed in a Test::More subtest block.  A TAP plan can be issued with
74           # "plan", and we can issue TODO or SKIP directives the same way.  There is
75           # none of the return-to-skip magic that we find in Test::Class.
76           #
77           # The string after "test" is used as the method name -- which means we're
78           # getting a method name with spaces in it.  This can be slightly problematic
79           # if you try to use, say, ::, in a method name.  For the most part, it works
80           # quite well -- but look at the next test for an example of how to give an
81           # explicit description.
82           test "only one key in hash" => sub {
83             my ($self) = @_;
84
85             my $hash = $self->hash_to_test;
86
87             is(keys %$hash, 1, "we have one key in our test hash");
88             is(2+2, 4, "universe still okay");
89           };
90
91           # The only thing of note here is that we're passing a hashref of extra args
92           # to the test method constructor.  "desc" lets us set the test's description,
93           # which is used in the test output, so we can avoid weird method names being
94           # installed.  Also note that we order tests more or less by order of
95           # definition, not by name or description.
96           test second_test => { desc => "Test::Routine demo!" } => sub {
97             pass("We're running this test second");
98             pass("...notice that the subtest's label is the 'desc' above");
99             pass("...and not the method name!");
100           };
101         }
102
103         {
104           # This package is one fixture against which we can run the HashTester
105           # routine.  It has the only thing it needs:  a build_hash_to_test method.
106           # Obviously real examples would have more to them than this.
107           package ProcessHash;
108           use Moose;
109           with 'HashTester';
110
111           use namespace::autoclean;
112
113           sub build_hash_to_test { return { $$ => $^T } }
114         }
115
116         # Now we're into the body of the test program:  where tests actually get run.
117
118         # We use Test::Routine::Util to get its "run_tests" routine, which runs the
119         # tests on an instance, building it if needed.
120         use Test::Routine::Util;
121
122         # We use Test::More to get done_testing.  We don't assume that run_tests is the
123         # entire test, because that way we can (as we do here) run multiple test
124         # instances, and can intersperse other kinds of sanity checks amongst the
125         # Test::Routine-style tests.
126         use Test::More;
127
128         is(2+2, 4, "universe still makes sense") or BAIL_OUT("PANIC!");
129
130         # The first arg is a description for the subtest that will be run.  The second,
131         # here, is a class that will be instantiated and tested.
132         run_tests('ProcessHash class' => 'ProcessHash');
133
134         # Here, the second argument is an instance of a class to test.
135         run_tests('ProcessHash obj' => ProcessHash->new({ hash_to_test => { 1 => 0 }}));
136
137         # We could also just supply a class name and a set of args to pass to new.
138         # The below is very nearly equivalent to the above:
139         run_tests('ProcessHash new' => ProcessHash => { hash_to_test => { 1 => 0 }});
140
141         # ...and here, the second arg is not a class or instance at all, but the
142         # Test::Routine role (by name).  Since we know we can't instantiate a role,
143         # run_tests will try to compose it with Moose::Object.  Then the args are used
144         # as the args to ->new on the new class, as above.  This lets us write
145         # Test::Routines that can be tested with the right state to start with, or
146         # Test::Routines that need to be composed with testing fixture classes.
147         run_tests(
148           'HashTester with given state',
149           HashTester => {
150             hash_to_test => { a => 1 },
151           },
152         );
153
154         # There's one more interesting way to run out tests, but it's demonstrated in
155         # 02-simple.t instead of here.  Go check that out.
156
157         # ...and we're done!
158         done_testing;
159
160   t/demo/02-simple.t
161         # Welcome to part two of the Test::Routine demo.  This is showing how you can
162         # write quick one-off tests without having to write a bunch of .pm files or
163         # (worse?) embed packages in bare blocks in the odious way that 01-demo.t did.
164         #
165         # First off, we use Test::Routine.  As it did before, this turns the current
166         # package (main!) into a Test::Routine role.  It also has the pleasant
167         # side-effect of turning on strict and warnings.
168         use Test::Routine;
169
170         # Then we bring in the utils, because we'll want to run_tests later.
171         use Test::Routine::Util;
172
173         # And, finally, we bring in Test::More so that we can use test assertions, and
174         # namespace::autoclean to clean up after us.
175         use Test::More;
176         use namespace::autoclean;
177
178         # We're going to give our tests some state.  It's nothing special.
179         has counter => (
180           is  => 'rw',
181           isa => 'Int',
182           default => 0,
183         );
184
185         # Then another boring but useful hunk of code: a method for our test routine.
186         sub counter_is_even {
187           my ($self) = @_;
188           return $self->counter % 2 == 0;
189         }
190
191         # Then we can write some tests, just like we did before.  Here, we're writing
192         # several tests, and they will be run in the order in which they were defined.
193         # You can see that they rely on the state being maintained.
194         test 'start even' => sub {
195           my ($self) = @_;
196           ok($self->counter_is_even, "we start with an even counter");
197
198           $self->counter( $self->counter + 1);
199         };
200
201         test 'terminate odd' => sub {
202           my ($self) = @_;
203
204           ok(! $self->counter_is_even, "the counter is odd, so state was preserved");
205           pass("for your information, the counter is " . $self->counter);
206         };
207
208         # Now we can run these tests just by saying "run_me" -- rather than expecting a
209         # class or role name, it uses the caller.  In this case, the calling package
210         # (main!) is a Test::Routine, so the runner composes it with Moose::Object,
211         # instantiating it, and running the tests on the instance.
212         run_me;
213
214         # Since each test run gets its own instance, we can run the test suite again,
215         # possibly to verify that the test suite is not destructive of some external
216         # state.
217         run_me("second run");
218
219         # And we can pass in args to use when constructing the object to be tested.
220         # Given the tests above, we can pick any starting value for "counter" that is
221         # even.
222         run_me({ counter => 192 });
223
224         # ...and we're done!
225         done_testing;
226
227         # More Test::Routine behavior is demonstrated in t/03-advice.t and t/04-misc.t
228         # Go have a look at those!
229
230   t/demo/03-advice.t
231         use Test::Routine;
232         use Test::Routine::Util;
233         use Test::More;
234
235         use namespace::autoclean;
236
237         # xUnit style testing has the idea of setup and teardown that happens around
238         # each test.  With Test::Routine, we assume that you will do most of this sort
239         # of thing in your BUILD, DEMOLISH, and attribute management.  Still, you can
240         # easily do setup and teardown by applying method modifiers to the "run_test"
241         # method, which your Test::Routine uses to run each test.  Here's a simple
242         # example.
243
244         # We have the same boring state that we saw before.  It's just an integer that
245         # is carried over between tests.
246         has counter => (
247           is   => 'rw',
248           isa  => 'Int',
249           lazy => 1,
250           default => 0,
251           clearer => 'clear_counter',
252         );
253
254         # The first test changes the counter's value and leaves it changed.
255         test test_0 => sub {
256           my ($self) = @_;
257
258           is($self->counter, 0, 'start with counter = 0');
259           $self->counter( $self->counter + 1);
260           is($self->counter, 1, 'end with counter = 1');
261         };
262
263         # The second test assumes that the value is the default, again.  We want to
264         # make sure that before each test, the counter is reset, but we don't want to
265         # tear down and recreate the whole object, because it may have other, more
266         # expensive resources built.
267         test test_1 => sub {
268           my ($self) = @_;
269
270           is($self->counter, 0, 'counter is reset between tests');
271         };
272
273         # ...so we apply a "before" modifier to each test run, calling the clearer on
274         # the counter.  When next accessed, it will re-initialize to zero.  We could
275         # call any other code we want here, and we can compose numerous modifiers
276         # together onto run_test.
277         #
278         # If you want to clear *all* the object state between each test... you probably
279         # want to refactor.
280         before run_test => sub { $_[0]->clear_counter };
281
282         run_me;
283         done_testing;
284
285   t/demo/04-misc.t
286         use Test::Routine;
287         use Test::Routine::Util;
288         use Test::More;
289
290         use namespace::autoclean;
291
292         # One thing that the previous examples didn't show was how to mark tests as
293         # "skipped" or "todo."  Test::Routine makes -no- provisions for these
294         # directives.  Instead, it assumes you will use the entirely usable mechanisms
295         # provided by Test::More.
296
297         # This is a normal test.  It is neither skipped nor todo.
298         test boring_ordinary_tests => sub {
299           pass("This is a plain old boring test that always passes.");
300           pass("It's here just to remind you what they look like.");
301         };
302
303         # To skip a test, we just add a "skip_all" plan.  Because test methods get run
304         # in subtests, this skips the whole subtest, but nothing else.
305         test sample_skip_test => sub {
306           plan skip_all => "these tests don't pass, for some reason";
307
308           is(6, 9, "I don't mind.");
309         };
310
311         # To mark a test todo, we just set our local $TODO variable.  Because the test
312         # is its own block, this works just like it would in any other Test::More test.
313         test sample_todo_test => sub {
314           local $TODO = 'demo of todo';
315
316           is(2 + 2, 5, "we can bend the fabric of reality");
317         };
318
319         run_me;
320         done_testing;
321
322   t/demo/05-multiple.t
323         #!/bin/env perl
324         use strict;
325         use warnings;
326
327         use Test::Routine::Util;
328         use Test::More;
329
330         # One of the benefits of building our sets of tests into roles instead of
331         # classes is that we can re-use them in whatever combination we want.  We can
332         # break down sets of tests into bits that can be re-used in different cases.
333         # With classes, this would lead to multiple inheritance or other monstrosities.
334
335         # Here's a first Test::Routine.  We use it to make sure that one of our
336         # fixture's attributes is a numeric id.
337         {
338           package Test::ThingHasID;
339           use Test::Routine;
340           use Test::More;
341
342           requires 'id';
343
344           test thing_has_numeric_id => sub {
345             my ($self) = @_;
346
347             my $id = $self->id;
348             like($id, qr/\A[0-9]+\z/, "the thing's id is a string of ascii digits");
349           };
350         }
351
352         # A second one ensures that the thing has an associated directory that
353         # looks like a unix path.
354         {
355           package Test::HasDirectory;
356           use Test::Routine;
357           use Test::More;
358
359           requires 'dir';
360
361           test thing_has_unix_dir => sub {
362             my ($self) = @_;
363
364             my $dir = $self->dir;
365             like($dir, qr{\A(?:/\w+)+/?\z}, "thing has a unix-like directory");
366           };
367         }
368
369         # We might have one class that is only expected to pass one test:
370         {
371           package JustHasID;
372           use Moose;
373
374           has id => (
375             is      => 'ro',
376             default => sub {
377               my ($self) = @_;
378               return Scalar::Util::refaddr($self);
379             },
380           );
381         }
382
383         # ...and another class that should pass both:
384         {
385           package UnixUser;
386           use Moose;
387
388           has id  => (is => 'ro', default => 501);
389           has dir => (is => 'ro', default => '/home/users/rjbs');
390         }
391
392         # So far, none of this is new, it's just a slightly different way of factoring
393         # things we've seen before.  In t/01-demo.t, we wrote distinct test roles and
394         # classes, and we made our class compose the role explicitly.  This can be
395         # a useful way to put these pieces together, but we also might want to write
396         # all these classes and roles as unconnected components and compose them only
397         # when we're ready to run our tests.  When we do that, we can tell run_tests
398         # what to put together.
399         #
400         # Here, we tell it that we can test JustHasID with Test::ThingHasID:
401         run_tests(
402           "our JustHasID objects have ids",
403           [ 'JustHasID', 'Test::ThingHasID' ],
404         );
405
406         # ...but we can run two test routines against our UnixUser class
407         run_tests(
408           "unix users have dirs and ids",
409           [ 'UnixUser', 'Test::ThingHasID', 'Test::HasDirectory' ],
410         );
411
412
413         # We can still use the "attributes to initialize an object," and when doing
414         # that it may be that we don't care to run all the otherwise applicable tests,
415         # because they're not interesting in the scenario we're creating.  For
416         # example...
417         run_tests(
418           "a trailing slash is okay in a directory",
419           [ 'UnixUser', 'Test::HasDirectory' ],
420           { dir => '/home/meebo/' },
421         );
422
423         # ...and we're done!
424         done_testing;
425

AUTHOR

427       Ricardo Signes <rjbs@cpan.org>
428
430       This software is copyright (c) 2010 by Ricardo Signes.
431
432       This is free software; you can redistribute it and/or modify it under
433       the same terms as the Perl 5 programming language system itself.
434
435
436
437perl v5.32.0                      2020-07-28    Test::Routine::Manual::Demo(3)
Impressum