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

PERL VERSION SUPPORT

12       This module has the same support period as perl itself:  it supports
13       the two most recent versions of perl.  (That is, if the most recently
14       released version is v5.40, then this module should work on both v5.40
15       and v5.38.)
16
17       Although it may work on older versions of perl, no guarantee is made
18       that the minimum required version will not be increased.  The version
19       may be increased for any reason, and there is no promise that patches
20       will be accepted to lower the minimum required perl.
21

The Demo

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

AUTHOR

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