1Test::Routine::Manual::UDseemro(C3o)ntributed Perl DocumTeenstta:t:iRoonutine::Manual::Demo(3)
2
3
4
6 Test::Routine::Manual::Demo - a walkthrough, in code, of Test::Routine
7
9 version 0.030
10
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
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
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)