File Coverage

blib/lib/Test/Module/Runnable/Base.pm
Criterion Covered Total %
statement 65 77 84.4
branch 19 32 59.3
condition 4 9 44.4
subroutine 10 11 90.9
pod 5 5 100.0
total 103 134 76.8


line stmt bran cond sub pod time code
1             # Module test framework
2             # Copyright (c) 2015-2017, Duncan Ross Palmer (2E0EOL) and others,
3             # All rights reserved.
4             #
5             # Redistribution and use in source and binary forms, with or without
6             # modification, are permitted provided that the following conditions are met:
7             #
8             # * Redistributions of source code must retain the above copyright notice,
9             # this list of conditions and the following disclaimer.
10             #
11             # * Redistributions in binary form must reproduce the above copyright
12             # notice, this list of conditions and the following disclaimer in the
13             # documentation and/or other materials provided with the distribution.
14             #
15             # * Neither the name of the Daybo Logic nor the names of its contributors
16             # may be used to endorse or promote products derived from this software
17             # without specific prior written permission.
18             #
19             # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20             # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21             # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22             # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23             # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24             # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25             # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26             # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27             # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28             # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29             # POSSIBILITY OF SUCH DAMAGE.
30              
31             =head1 NAME
32              
33             Test::Module::Runnable - A runnable framework on Moose for running tests
34              
35             =head1 SYNOPSIS
36              
37             package YourTestSuite;
38             use Moose;
39             use Test::More 0.96;
40              
41             extends 'Test::Module::Runnable';
42              
43             sub helper { } # Not called
44              
45             sub testExample { } # Automagically called due to 'test' prefix.
46              
47             package main;
48              
49             my $tester = new YourTestSuite;
50             plan tests => $tester->testCount;
51             foreach my $name ($tester->testMethods) {
52             subtest $name => $tester->$name;
53             }
54              
55             alternatively...
56              
57             my $tester = new YourTestSuite;
58             return $tester->run;
59              
60             =head1 DESCRIPTION
61              
62             A test framework based on Moose introspection to automagically
63             call all methods matching a user-defined pattern. Supports per-test
64             setup and tear-down routines and easy early L<Test::Builder/BAIL_OUT> using
65             L<Test::More>.
66              
67             =cut
68              
69             package Test::Module::Runnable::Base;
70              
71 4     4   2269 use Moose;
  4         12  
  4         34  
72 4     4   27437 use Test::More 0.96;
  4         75  
  4         25  
73 4     4   1006 use POSIX qw/EXIT_SUCCESS/;
  4         8  
  4         27  
74              
75             BEGIN {
76 4     4   3610 our $VERSION = '0.2.3';
77             }
78              
79             =head1 ATTRIBUTES
80              
81             =over
82              
83             =item C<sut>
84              
85             System under test - a generic slot for an object you are testing, which
86             could be re-initialized under the C<setUp> routine, but this entry may be
87             ignored.
88              
89             =back
90              
91             =cut
92              
93             has 'sut' => (is => 'rw', required => 0);
94              
95             =head1 PRIVATE ATTRIBUTES
96              
97             =over
98              
99             =item C<__unique_default_domain>
100              
101             The internal default domain value. This is used when C<unique>
102             is called without a domain, because a key cannot be C<undef> in Perl.
103              
104             =cut
105              
106             has '__unique_default_domain' => (
107             isa => 'Str',
108             is => 'ro',
109             default => 'db3eb5cf-a597-4038-aea8-fd06faea6eed'
110             );
111              
112             =item C<__unique>
113              
114             Tracks the counter returned by C<unique>.
115             Always contains the previous value returned, or zero before any calls.
116             A hash is used to support multiple domains.
117              
118             =cut
119              
120             has '__unique' => (
121             is => 'ro',
122             isa => 'HashRef[Int]',
123             default => sub {
124             { }
125             },
126             );
127              
128             =item C<__random>
129              
130             Hash of random numbers already given out.
131              
132             =back
133              
134             =cut
135              
136             has '__random' => (
137             is => 'ro',
138             isa => 'HashRef[Int]',
139             default => sub {
140             { }
141             },
142             );
143              
144             =head1 METHODS
145              
146             =over
147              
148             =item C<setUpBeforeClass>
149              
150             Placeholder method called before any test method is called, in order
151             for you to initialize your tests.
152              
153             =item C<unique>
154              
155             Returns a unique, integer ID, which is predictable.
156              
157             An optional C<$domain> can be specified, which is a discrete sequence,
158             isolated from anhy other domain. If not specified, a default domain is used.
159             The actual name for this domain is opaque, and is specified by
160             L</__unique_default_domain>.
161              
162             A special domain; C<rand> can be used for random numbers which will not repeat.
163              
164             =cut
165              
166             sub unique {
167 507     507 1 1024 my ($self, $domain) = @_;
168 507         694 my $useRandomDomain = 0;
169 507         633 my $result;
170              
171 507 100 100     1889 if (defined($domain) && length($domain)) {
172 503 100       1015 $useRandomDomain++ if ('rand' eq $domain);
173             } else {
174 4         108 $domain = $self->__unique_default_domain;
175             }
176              
177 507 100       825 if ($useRandomDomain) {
178             do {
179 500         13362 $result = int(rand(999_999_999));
180 500         624 } while ($self->__random->{$result});
181 500         11640 $self->__random->{$result}++;
182             } else {
183 7         169 $result = ++($self->__unique->{$domain});
184             }
185              
186 507         1567 return $result;
187             }
188              
189             =item C<pattern>
190              
191             The pattern which defines which user-methods are considered tests.
192             Defaults to ^test
193             Methods matching this pattern will be returned from L</methodNames>
194              
195             =cut
196              
197             has 'pattern' => (is => 'ro', isa => 'Regexp', default => sub { qr/^test/ });
198              
199             =item C<logger>
200              
201             A generic slot for a loggger, to be initialized with your logging framework,
202             or a mock logging system.
203              
204             This slot is not touched by this package, but might be passed on to
205             your L</sut>, or you may wish to clear it between tests by sub-classing
206             this package.
207              
208             =cut
209              
210             has 'logger' => (is => 'rw', required => 0);
211              
212             =item C<mocker>
213              
214             This slot can be used during L</setUpBeforeClass> to set up a C<Test::MockModule>
215             for the C<sut> class being tested. If set, C<mocker->unmock_all()> will be
216             called automagically, just after each test method is executed.
217             This will allow different methods to to be mocked, which are not directly relevant
218             to the test method being executed.
219              
220             By default, this slot is C<undef>
221              
222             =cut
223              
224             has 'mocker' => (
225             is => 'rw',
226             isa => 'Maybe[Test::MockModule]',
227             required => 0,
228             default => undef,
229             );
230              
231             =item C<methodNames>
232              
233             Returns a list of all names of test methods which should be called by C<subtest>,
234             ie. all method names beginning with 'test', or the user-defined C<pattern>.
235              
236             If you use C<run>, this is handled automagically.
237              
238             =cut
239              
240             sub methodNames {
241 6     6 1 1888 my @ret = ( );
242 6         10 my $self = shift;
243 6         26 my @methodList = $self->meta->get_all_methods();
244              
245 6         21025 foreach my $method (@methodList) {
246 220         472 $method = $method->name;
247 220 50       609 next unless ($self->can($method)); # Skip stuff we cannot do
248 220 100       5078 next if ($method !~ $self->pattern); # Skip our own helpers
249 11         27 push(@ret, $method);
250             }
251              
252 6         33 return @ret;
253             }
254              
255             =item C<methodCount>
256              
257             Returns the number of tests to pass to C<plan>
258             If you use C<run>, this is handled automagically.
259              
260             =cut
261              
262             sub methodCount {
263 2     2 1 322 my $self = shift;
264 2         6 return scalar($self->methodNames());
265             }
266              
267             sub __wrapFail {
268 111     111   231 my ($self, $type, $method, $returnValue) = @_;
269 111 50 33     459 return if (defined($returnValue) && $returnValue eq '0');
270 0 0       0 if (!defined($method)) { # Not method-specific
271 0 0 0     0 BAIL_OUT('Must specify type when evaluating result from method hooks')
272             if ('setUpBeforeClass' ne $type && 'tearDownAfterClass' ne $type);
273              
274 0         0 $method = 'N/A';
275             }
276 0         0 BAIL_OUT($type . ' returned non-zero for ' . $method);
277             }
278              
279             =item C<run>
280              
281             Executes all of the tests, in a random order
282             An optional override may be passed with the tests parameter.
283              
284             * tests
285             An ARRAY ref which contains the inclusive list of all tests
286             to run. If not passed, all tests are run. If an empty list
287             is passed, no tests are run. If a test does not exist, C<confess>
288             is called.
289              
290             * n
291             Number of times to iterate through the tests.
292             Defaults to 1. Setting to a higher level is useful if you want to
293             prove that the random ordering of tests does not break, but you do
294             not want to type 'make test' many times.
295              
296             Returns:
297             The return value is always C<EXIT_SUCCESS>, which you can pass straight
298             to C<exit>
299              
300             =cut
301              
302             sub run {
303 3     3 1 4980 my ($self, %params) = @_;
304 3         8 my ($fail, @tests) = (0);
305              
306 3 100       21 $params{n} = 1 unless ($params{n});
307              
308 3 50       14 if (ref($params{tests}) eq 'ARRAY') { # User specified
309 0         0 @tests = @{ $params{tests} };
  0         0  
310             } else {
311 3         13 @tests = $self->methodNames();
312 3 100       12 if (@ARGV) {
313 1         3 my @userRunTests = ( );
314 1         3 foreach my $testName (@tests) {
315 1         2 foreach my $arg (@ARGV) {
316 2 50       6 next if ($arg ne $testName);
317 0         0 push(@userRunTests, $testName);
318             }
319             }
320              
321 1 50       4 if (scalar(@userRunTests) > 0) {
322 0         0 @tests = @userRunTests;
323             }
324             }
325             }
326              
327 3         21 plan tests => scalar(@tests) * $params{n};
328              
329 3         2137 $fail = $self->setUpBeforeClass(); # Call any registered pre-suite routine
330 3         102 $self->__wrapFail('setUpBeforeClass', undef, $fail);
331 3         14 for (my $i = 0; $i < $params{n}; $i++) {
332 18         36 foreach my $method (@tests) {
333 35         50 $fail = 0;
334              
335             # Check if user specified just one test, and this isn't it
336 35 50       116 confess(sprintf('Test \'%s\' does not exist', $method))
337             unless $self->can($method);
338              
339 35         96 $fail = $self->setUp(method => $method); # Call any registered pre-test routine
340 35         754 $self->__wrapFail('setUp', $method, $fail);
341 35     35   188 subtest $method => sub { $fail = $self->$method(method => $method) }; # Correct test (or all)
  35         22905  
342 35         71491 $self->__wrapFail('method', $method, $fail);
343 35 50       1126 $self->mocker->unmock_all() if ($self->mocker);
344 35         56 $fail = 0;
345 35         98 $fail = $self->tearDown(method => $method); # Call any registered post-test routine
346 35         809 $self->__wrapFail('tearDown', $method, $fail);
347             }
348             }
349 3         18 $fail = $self->tearDownAfterClass(); # Call any registered post-suite routine
350 3         13 $self->__wrapFail('tearDownAfterClass', undef, $fail);
351              
352 3         28 return EXIT_SUCCESS;
353             }
354              
355             =item C<debug>
356              
357             Call C<Test::Builder::diag> with a user-defined message,
358             if and only if the C<TEST_VERBOSE> environment variable is set.
359              
360             =cut
361              
362             sub debug {
363 0     0 1   my (undef, $format, @params) = @_;
364 0 0         return unless ($ENV{'TEST_VERBOSE'});
365 0           diag(sprintf($format, @params));
366 0           return;
367             }
368              
369             =back
370              
371             =head1 AUTHOR
372              
373             Duncan Ross Palmer, 2E0EOL L<mailto:palmer@overchat.org>
374              
375             =head1 LICENCE
376              
377             Daybo Logic Shared Library
378             Copyright (c) 2015-2017, Duncan Ross Palmer (2E0EOL), Daybo Logic
379             All rights reserved.
380              
381             Redistribution and use in source and binary forms, with or without
382             modification, are permitted provided that the following conditions are met:
383              
384             * Redistributions of source code must retain the above copyright notice,
385             this list of conditions and the following disclaimer.
386              
387             * Redistributions in binary form must reproduce the above copyright
388             notice, this list of conditions and the following disclaimer in the
389             documentation and/or other materials provided with the distribution.
390              
391             * Neither the name of the Daybo Logic nor the names of its contributors
392             may be used to endorse or promote products derived from this software
393             without specific prior written permission.
394              
395             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
396             AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
397             IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
398             ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
399             LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
400             CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
401             SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
402             INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
403             CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
404             ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
405             POSSIBILITY OF SUCH DAMAGE.
406              
407             =head1 AVAILABILITY
408              
409             L<https://bitbucket.org/2E0EOL/libtest-module-runnable-perl>
410              
411             =head1 CAVEATS
412              
413             None known.
414              
415             =cut
416              
417             1;