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.027
10
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
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)