1TAP::Harness::Beyond(3)User Contributed Perl DocumentatioTnAP::Harness::Beyond(3)
2
3
4
6 Test::Harness is responsible for running test scripts, analysing their
7 output and reporting success or failure. When I type make test (or
8 ./Build test) for a module, Test::Harness is usually used to run the
9 tests (not all modules use Test::Harness but the majority do).
10
11 To start exploring some of the features of Test::Harness I need to
12 switch from make test to the prove command (which ships with
13 Test::Harness). For the following examples I'll also need a recent
14 version of Test::Harness installed; 3.14 is current as I write.
15
16 For the examples I'm going to assume that we're working with a 'normal'
17 Perl module distribution. Specifically I'll assume that typing make or
18 ./Build causes the built, ready-to-install module code to be available
19 below ./blib/lib and ./blib/arch and that there's a directory called
20 't' that contains our tests. Test::Harness isn't hardwired to that
21 configuration but it saves me from explaining which files live where
22 for each example.
23
24 Back to prove; like make test it runs a test suite - but it provides
25 far more control over which tests are executed, in what order and how
26 their results are reported. Typically make test runs all the test
27 scripts below the 't' directory. To do the same thing with prove I
28 type:
29
30 prove -rb t
31
32 The switches here are -r to recurse into any directories below 't' and
33 -b which adds ./blib/lib and ./blib/arch to Perl's include path so that
34 the tests can find the code they will be testing. If I'm testing a
35 module of which an earlier version is already installed I need to be
36 careful about the include path to make sure I'm not running my tests
37 against the installed version rather than the new one that I'm working
38 on.
39
40 Unlike make test, typing prove doesn't automatically rebuild my module.
41 If I forget to make before prove I will be testing against older
42 versions of those files - which inevitably leads to confusion. I
43 either get into the habit of typing
44
45 make && prove -rb t
46
47 or - if I have no XS code that needs to be built I use the modules
48 below lib instead
49
50 prove -Ilib -r t
51
52 So far I've shown you nothing that make test doesn't do. Let's fix
53 that.
54
55 Saved State
56 If I have failing tests in a test suite that consists of more than a
57 handful of scripts and takes more than a few seconds to run it rapidly
58 becomes tedious to run the whole test suite repeatedly as I track down
59 the problems.
60
61 I can tell prove just to run the tests that are failing like this:
62
63 prove -b t/this_fails.t t/so_does_this.t
64
65 That speeds things up but I have to make a note of which tests are
66 failing and make sure that I run those tests. Instead I can use prove's
67 --state switch and have it keep track of failing tests for me. First I
68 do a complete run of the test suite and tell prove to save the results:
69
70 prove -rb --state=save t
71
72 That stores a machine readable summary of the test run in a file called
73 '.prove' in the current directory. If I have failures I can then run
74 just the failing scripts like this:
75
76 prove -b --state=failed
77
78 I can also tell prove to save the results again so that it updates its
79 idea of which tests failed:
80
81 prove -b --state=failed,save
82
83 As soon as one of my failing tests passes it will be removed from the
84 list of failed tests. Eventually I fix them all and prove can find no
85 failing tests to run:
86
87 Files=0, Tests=0, 0 wallclock secs ( 0.00 usr + 0.00 sys = 0.00 CPU)
88 Result: NOTESTS
89
90 As I work on a particular part of my module it's most likely that the
91 tests that cover that code will fail. I'd like to run the whole test
92 suite but have it prioritize these 'hot' tests. I can tell prove to do
93 this:
94
95 prove -rb --state=hot,save t
96
97 All the tests will run but those that failed most recently will be run
98 first. If no tests have failed since I started saving state all tests
99 will run in their normal order. This combines full test coverage with
100 early notification of failures.
101
102 The --state switch supports a number of options; for example to run
103 failed tests first followed by all remaining tests ordered by the
104 timestamps of the test scripts - and save the results - I can use
105
106 prove -rb --state=failed,new,save t
107
108 See the prove documentation (type prove --man) for the full list of
109 state options.
110
111 When I tell prove to save state it writes a file called '.prove'
112 ('_prove' on Windows) in the current directory. It's a YAML document so
113 it's quite easy to write tools of your own that work on the saved test
114 state - but the format isn't officially documented so it might change
115 without (much) warning in the future.
116
117 Parallel Testing
118 If my tests take too long to run I may be able to speed them up by
119 running multiple test scripts in parallel. This is particularly
120 effective if the tests are I/O bound or if I have multiple CPU cores. I
121 tell prove to run my tests in parallel like this:
122
123 prove -rb -j 9 t
124
125 The -j switch enables parallel testing; the number that follows it is
126 the maximum number of tests to run in parallel. Sometimes tests that
127 pass when run sequentially will fail when run in parallel. For example
128 if two different test scripts use the same temporary file or attempt to
129 listen on the same socket I'll have problems running them in parallel.
130 If I see unexpected failures I need to check my tests to work out which
131 of them are trampling on the same resource and rename temporary files
132 or add locks as appropriate.
133
134 To get the most performance benefit I want to have the test scripts
135 that take the longest to run start first - otherwise I'll be waiting
136 for the one test that takes nearly a minute to complete after all the
137 others are done. I can use the --state switch to run the tests in
138 slowest to fastest order:
139
140 prove -rb -j 9 --state=slow,save t
141
142 Non-Perl Tests
143 The Test Anything Protocol (http://testanything.org/) isn't just for
144 Perl. Just about any language can be used to write tests that output
145 TAP. There are TAP based testing libraries for C, C++, PHP, Python and
146 many others. If I can't find a TAP library for my language of choice
147 it's easy to generate valid TAP. It looks like this:
148
149 1..3
150 ok 1 - init OK
151 ok 2 - opened file
152 not ok 3 - appended to file
153
154 The first line is the plan - it specifies the number of tests I'm going
155 to run so that it's easy to check that the test script didn't exit
156 before running all the expected tests. The following lines are the test
157 results - 'ok' for pass, 'not ok' for fail. Each test has a number and,
158 optionally, a description. And that's it. Any language that can produce
159 output like that on STDOUT can be used to write tests.
160
161 Recently I've been rekindling a two-decades-old interest in Forth.
162 Evidently I have a masochistic streak that even Perl can't satisfy. I
163 want to write tests in Forth and run them using prove (you can find my
164 gforth TAP experiments at https://svn.hexten.net/andy/Forth/Testing/).
165 I can use the --exec switch to tell prove to run the tests using gforth
166 like this:
167
168 prove -r --exec gforth t
169
170 Alternately, if the language used to write my tests allows a shebang
171 line I can use that to specify the interpreter. Here's a test written
172 in PHP:
173
174 #!/usr/bin/php
175 <?php
176 print "1..2\n";
177 print "ok 1\n";
178 print "not ok 2\n";
179 ?>
180
181 If I save that as t/phptest.t the shebang line will ensure that it runs
182 correctly along with all my other tests.
183
184 Mixing it up
185 Subtle interdependencies between test programs can mask problems - for
186 example an earlier test may neglect to remove a temporary file that
187 affects the behaviour of a later test. To find this kind of problem I
188 use the --shuffle and --reverse options to run my tests in random or
189 reversed order.
190
191 Rolling My Own
192 If I need a feature that prove doesn't provide I can easily write my
193 own.
194
195 Typically you'll want to change how TAP gets input into and output from
196 the parser. App::Prove supports arbitrary plugins, and TAP::Harness
197 supports custom formatters and source handlers that you can load using
198 either prove or Module::Build; there are many examples to base mine on.
199 For more details see App::Prove, TAP::Parser::SourceHandler, and
200 TAP::Formatter::Base.
201
202 If writing a plugin is not enough, you can write your own test harness;
203 one of the motives for the 3.00 rewrite of Test::Harness was to make it
204 easier to subclass and extend.
205
206 The Test::Harness module is a compatibility wrapper around
207 TAP::Harness. For new applications I should use TAP::Harness directly.
208 As we'll see, prove uses TAP::Harness.
209
210 When I run prove it processes its arguments, figures out which test
211 scripts to run and then passes control to TAP::Harness to run the
212 tests, parse, analyse and present the results. By subclassing
213 TAP::Harness I can customise many aspects of the test run.
214
215 I want to log my test results in a database so I can track them over
216 time. To do this I override the summary method in TAP::Harness. I
217 start with a simple prototype that dumps the results as a YAML
218 document:
219
220 package My::TAP::Harness;
221
222 use base qw( TAP::Harness ); use YAML;
223
224 sub summary {
225 my ( $self, $aggregate ) = @_;
226 print Dump( $aggregate );
227 $self->SUPER::summary( $aggregate );
228 }
229
230 1;
231
232 I need to tell prove to use my My::TAP::Harness. If My::TAP::Harness is
233 on Perl's @INC include path I can
234
235 prove --harness=My::TAP::Harness -rb t
236
237 If I don't have My::TAP::Harness installed on @INC I need to provide
238 the correct path to perl when I run prove:
239
240 perl -Ilib `which prove` --harness=My::TAP::Harness -rb t
241
242 I can incorporate these options into my own version of prove. It's
243 pretty simple. Most of the work of prove is handled by App::Prove. The
244 important code in prove is just:
245
246 use App::Prove;
247
248 my $app = App::Prove->new;
249 $app->process_args(@ARGV);
250 exit( $app->run ? 0 : 1 );
251
252 If I write a subclass of App::Prove I can customise any aspect of the
253 test runner while inheriting all of prove's behaviour. Here's myprove:
254
255 #!/usr/bin/env perl use lib qw( lib ); # Add ./lib to @INC
256 use App::Prove;
257
258 my $app = App::Prove->new;
259
260 # Use custom TAP::Harness subclass
261 $app->harness( 'My::TAP::Harness' );
262
263 $app->process_args( @ARGV ); exit( $app->run ? 0 : 1 );
264
265 Now I can run my tests like this
266
267 ./myprove -rb t
268
269 Deeper Customisation
270 Now that I know how to subclass and replace TAP::Harness I can replace
271 any other part of the harness. To do that I need to know which classes
272 are responsible for which functionality. Here's a brief guided tour;
273 the default class for each component is shown in parentheses. Normally
274 any replacements I write will be subclasses of these default classes.
275
276 When I run my tests TAP::Harness creates a scheduler
277 (TAP::Parser::Scheduler) to work out the running order for the tests,
278 an aggregator (TAP::Parser::Aggregator) to collect and analyse the test
279 results and a formatter (TAP::Formatter::Console) to display those
280 results.
281
282 If I'm running my tests in parallel there may also be a multiplexer
283 (TAP::Parser::Multiplexer) - the component that allows multiple tests
284 to run simultaneously.
285
286 Once it has created those helpers TAP::Harness starts running the
287 tests. For each test it creates a new parser (TAP::Parser) which is
288 responsible for running the test script and parsing its output.
289
290 To replace any of these components I call one of these harness methods
291 with the name of the replacement class:
292
293 aggregator_class
294 formatter_class
295 multiplexer_class
296 parser_class
297 scheduler_class
298
299 For example, to replace the aggregator I would
300
301 $harness->aggregator_class( 'My::Aggregator' );
302
303 Alternately I can supply the names of my substitute classes to the
304 TAP::Harness constructor:
305
306 my $harness = TAP::Harness->new(
307 { aggregator_class => 'My::Aggregator' }
308 );
309
310 If I need to reach even deeper into the internals of the harness I can
311 replace the classes that TAP::Parser uses to execute test scripts and
312 tokenise their output. Before running a test script TAP::Parser creates
313 a grammar (TAP::Parser::Grammar) to decode the raw TAP into tokens, a
314 result factory (TAP::Parser::ResultFactory) to turn the decoded TAP
315 results into objects and, depending on whether it's running a test
316 script or reading TAP from a file, scalar or array a source or an
317 iterator (TAP::Parser::IteratorFactory).
318
319 Each of these objects may be replaced by calling one of these parser
320 methods:
321
322 source_class
323 perl_source_class
324 grammar_class
325 iterator_factory_class
326 result_factory_class
327
328 Callbacks
329 As an alternative to subclassing the components I need to change I can
330 attach callbacks to the default classes. TAP::Harness exposes these
331 callbacks:
332
333 parser_args Tweak the parameters used to create the parser
334 made_parser Just made a new parser
335 before_runtests About to run tests
336 after_runtests Have run all tests
337 after_test Have run an individual test script
338
339 TAP::Parser also supports callbacks; bailout, comment, plan, test,
340 unknown, version and yaml are called for the corresponding TAP result
341 types, ALL is called for all results, ELSE is called for all results
342 for which a named callback is not installed and EOF is called once at
343 the end of each TAP stream.
344
345 To install a callback I pass the name of the callback and a subroutine
346 reference to TAP::Harness or TAP::Parser's callback method:
347
348 $harness->callback( after_test => sub {
349 my ( $script, $desc, $parser ) = @_;
350 } );
351
352 I can also pass callbacks to the constructor:
353
354 my $harness = TAP::Harness->new({
355 callbacks => {
356 after_test => sub {
357 my ( $script, $desc, $parser ) = @_;
358 # Do something interesting here
359 }
360 }
361 });
362
363 When it comes to altering the behaviour of the test harness there's
364 more than one way to do it. Which way is best depends on my
365 requirements. In general if I only want to observe test execution
366 without changing the harness' behaviour (for example to log test
367 results to a database) I choose callbacks. If I want to make the
368 harness behave differently subclassing gives me more control.
369
370 Parsing TAP
371 Perhaps I don't need a complete test harness. If I already have a TAP
372 test log that I need to parse all I need is TAP::Parser and the various
373 classes it depends upon. Here's the code I need to run a test and parse
374 its TAP output
375
376 use TAP::Parser;
377
378 my $parser = TAP::Parser->new( { source => 't/simple.t' } );
379 while ( my $result = $parser->next ) {
380 print $result->as_string, "\n";
381 }
382
383 Alternately I can pass an open filehandle as source and have the parser
384 read from that rather than attempting to run a test script:
385
386 open my $tap, '<', 'tests.tap'
387 or die "Can't read TAP transcript ($!)\n";
388 my $parser = TAP::Parser->new( { source => $tap } );
389 while ( my $result = $parser->next ) {
390 print $result->as_string, "\n";
391 }
392
393 This approach is useful if I need to convert my TAP based test results
394 into some other representation. See TAP::Convert::TET
395 (http://search.cpan.org/dist/TAP-Convert-TET/) for an example of this
396 approach.
397
398 Getting Support
399 The Test::Harness developers hang out on the tapx-dev mailing list[1].
400 For discussion of general, language independent TAP issues there's the
401 tap-l[2] list. Finally there's a wiki dedicated to the Test Anything
402 Protocol[3]. Contributions to the wiki, patches and suggestions are all
403 welcome.
404
405 [1] <http://www.hexten.net/mailman/listinfo/tapx-dev> [2]
406 <http://testanything.org/mailman/listinfo/tap-l> [3]
407 <http://testanything.org/>
408
409
410
411perl v5.16.3 2013-05-02 TAP::Harness::Beyond(3)