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.030
10

PERL VERSION

12       This module should work on any version of perl still receiving updates
13       from the Perl 5 Porters.  This means it should work on any version of
14       perl released in the last two to three years.  (That is, if the most
15       recently released version is v5.40, then this module should work on
16       both v5.40 and v5.38.)
17
18       Although it may work on older versions of perl, no guarantee is made
19       that the minimum required version will not be increased.  The version
20       may be increased for any reason, and there is no promise that patches
21       will be accepted to lower the minimum required perl.
22

The Demo

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

AUTHOR

439       Ricardo Signes <cpan@semiotic.systems>
440
442       This software is copyright (c) 2010 by Ricardo Signes.
443
444       This is free software; you can redistribute it and/or modify it under
445       the same terms as the Perl 5 programming language system itself.
446
447
448
449perl v5.36.0                      2023-01-20    Test::Routine::Manual::Demo(3)
Impressum