File Coverage

blib/lib/Test/Subtest/Attribute.pm
Criterion Covered Total %
statement 43 79 54.4
branch 7 24 29.1
condition 2 31 6.4
subroutine 13 16 81.2
pod 7 8 87.5
total 72 158 45.5


line stmt bran cond sub pod time code
1             package Test::Subtest::Attribute;
2              
3             # ABSTRACT: Declare subtests using subroutine attributes
4              
5 1     1   4194 use 5.006;
  1         2  
6 1     1   5 use strict;
  1         2  
  1         18  
7 1     1   4 use warnings;
  1         2  
  1         29  
8              
9              
10 1     1   360 use Attribute::Handlers;
  1         2640  
  1         5  
11 1     1   32 use Test::Builder qw();
  1         3  
  1         16  
12              
13 1     1   4 use base qw( Exporter );
  1         2  
  1         329  
14              
15             our @EXPORT_OK = qw(
16             subtests
17             );
18             our $VERSION = '0.02';
19              
20             my @subtests;
21             my $builder;
22             my $unknown_sub_count = 0;
23              
24             sub UNIVERSAL::Subtest : ATTR(CODE) { ## no critic (Capitalization)
25 0     0 0 0 my ( $package, $symbol, $referent, $attr, $data ) = @_;
26              
27 0         0 my $sub_name;
28 0 0       0 if ( ref $symbol ) {
29 0         0 $sub_name = *{ $symbol }{NAME};
  0         0  
30             }
31              
32 0 0       0 my @args = ref $data ? @{ $data } : ();
  0         0  
33 0         0 my ( $name, $append_prepend ) = @args;
34 0   0     0 $append_prepend ||= 'append';
35 0 0 0     0 if ( $sub_name && ! $name ) {
36 0         0 $name = $sub_name;
37 0         0 $name =~ s/ ^ subtest_ //msx;
38             }
39              
40 0         0 my %args = (
41             coderef => $referent,
42             data => $data,
43             name => $name,
44             'package' => $package,
45             sub_name => $sub_name,
46             symbol => $symbol,
47             where => $append_prepend,
48             );
49              
50 0         0 subtests()->add( %args );
51              
52 0         0 return 1;
53 1     1   8 }
  1         2  
  1         3  
54              
55              
56             sub subtests {
57 11     11 1 513327 return __PACKAGE__;
58             }
59              
60              
61              
62             sub add {
63 2     2 1 7 my ( $self, %args ) = @_;
64              
65 2   33     8 $args{name} ||= $args{sub_name};
66 2 50       4 if ( ! $args{name} ) {
67 0         0 $unknown_sub_count++;
68 0         0 $args{name} = '__unknown_subtest' . $unknown_sub_count;
69             }
70              
71             # If we have a subtest with the same name as one that's already in our list,
72             # replace it. This allows derived classes to override the subtests in
73             # parent classes.
74 2         6 foreach my $subtest ( @subtests ) {
75 1 50       6 if ( $subtest->{name} eq $args{name} ) {
76 0         0 $subtest = \%args;
77 0         0 return 1;
78             }
79             }
80              
81 2   50     17 $args{where} ||= 'append';
82 2 100       7 if ( $args{where} eq 'prepend' ) {
83 1         4 unshift @subtests, { %args };
84             }
85             else {
86 1         3 push @subtests, { %args };
87             }
88              
89 2         17 return 1;
90             }
91              
92              
93             sub prepend {
94 1     1 1 6 my ( $self, %args ) = @_;
95              
96 1         4 return subtests()->add( %args, where => 'prepend' );
97             }
98              
99              
100             sub append {
101 1     1 1 6 my ( $self, %args ) = @_;
102              
103 1         9 return subtests()->add( %args, where => 'append' );
104             }
105              
106              
107             sub remove {
108 2     2 1 5 my ( $self, $which ) = @_;
109              
110 2 50       7 return if ! $which;
111              
112 2 100       14 my $field = ref $which ? 'coderef' : 'name';
113 2         6 my @clean = grep { $_->{ $field } ne $which } @subtests;
  3         16  
114 2         4 @subtests = @clean;
115              
116 2         10 return 1;
117             }
118              
119              
120             sub get_all {
121 5     5 1 13 return @subtests;
122             }
123              
124              
125             sub run {
126 0     0 1   my ( $self, %args ) = @_;
127              
128 0   0       $builder ||= $args{builder} || Test::Builder->new();
      0        
129              
130 0           foreach my $subtest ( @subtests ) {
131 0   0       my $invocant = $args{invocant} || $subtest->{package} || 'main';
132 0   0       my $name = $subtest->{name} || '(unknown)';
133 0 0         if ( $args{verbose_names} ) {
134 0   0       my $sub_name = $subtest->{sub_name} || '(unknown sub)';
135 0           my $package_name = $subtest->{package};
136 0 0 0       my $verbose_name = ( $package_name && $package_name ne 'main' )
137             ? "${package_name}::${sub_name}"
138             : $sub_name;
139 0           $name .= " [$verbose_name]";
140             }
141              
142 0           my $subref = $subtest->{coderef};
143 0 0 0       if ( $subtest->{sub_name} && ! $subref ) {
144 0           $subref = $invocant->can( $subtest->{sub_name} );
145             }
146 0 0 0       if ( $subref && ref $subref eq 'CODE' ) {
147 0     0     $builder->subtest( $name, sub { return $invocant->$subref(); } );
  0            
148             }
149             }
150              
151 0           return 1;
152             }
153              
154             1;
155              
156             __END__
157              
158             =pod
159              
160             =encoding UTF-8
161              
162             =head1 NAME
163              
164             Test::Subtest::Attribute - Declare subtests using subroutine attributes
165              
166             =head1 VERSION
167              
168             version 0.02
169              
170             =head1 SYNOPSIS
171              
172             use Test::More;
173             use Test::Subtest::Attribute qw( subtests );
174            
175             sub subtest_foo :Subtest {
176             ok( 1, 'foo is OK' );
177             return 1;
178             }
179            
180             sub subtest_bar :Subtest( 'name for bar' ) {
181             ok( 1, 'bar is OK' );
182             return 1;
183             }
184              
185             subtests()->run();
186             done_testing();
187              
188             =head1 DESCRIPTION
189              
190             This module provides a simple way, using a subroutine attribute called C<:Subtest>, to declare normal subroutines to be subtests in a test script.
191              
192             Subtests are typically declared using a call to the C<subtest()> function from L<Test::More>, in one of the two following ways:
193              
194             subtest 'name1' => sub { ... }; # An anonymous sub
195             subtest 'name 2' => \&some_named_sub;
196              
197             The first way can quickly lead to long anonymous subs that can present issues when looking at stacktraces for debugging, profiling, logging, etc.
198             The second way usually leads to repeating the same, or similar, names for each subtest subroutine, in addition to declaring the sub itself, e.g.:
199              
200             subtest 'test_this' => \&test_this;
201             subtest 'test_that' => \&test_that;
202             ...
203             sub test_this { ... }
204             sub test_that { ... }
205             ...
206              
207             This module lets you declare those subtests without calls to the C<subtest()> function, by simply adding a C<:Subtest> attribute to any
208             subroutine that you'd like to have executed as a subtest, like so:
209              
210             sub subtest_name1 :Subtest {
211             ...
212             }
213              
214             That declares a subtest named 'name1' (the subtest_ part of the name, if present, is automatically stripped off).
215              
216             If you'd like to specify the name of the subtest explicitly, which is handy if you'd like to use a name that includes characters. such as spaces,
217             that aren't allowed in bareword identifiers, you can do so by providing an argument to the C<:Subtest> attribute like so:
218              
219             sub some_named_sub :Subtest('name 2') {
220             ...
221             }
222              
223             When you're done declaring subtests, you run all the ones you've queued up by calling C<subtests()->run()>.
224              
225             From this module, most test scripts will only need to use the C<:Subtest> attribute and the C<run()> method described below.
226             Most of the other methods described below are for more advanced usage, such as in test modules that might want to conditionally
227             add, remove, or otherwise manipulate the subtests managed herein.
228              
229             =head1 METHODS
230              
231             =head2 add
232              
233             subtests()->add( coderef => \%my_sub );
234              
235             Adds a subroutine to the current queue of subtests.
236             This method can accept a number of named arguments.
237              
238             =over
239              
240             =item name
241              
242             Indicates the name of this particular subtest.
243             If the name isn't unique, it will replace the previously declared subtest with the same name.
244              
245             =item where
246              
247             A value of 'prepend' indicates the subtest should be added to the head of the queue of subtests.
248             A value of 'append' indicates the subtest should be added to the end of the queue of subtests.
249             If not given, the default is to append the subtest.
250              
251             =item coderef
252              
253             A reference to the subroutine (named or anonymous) to eventually call for this subtest.
254              
255             =item package
256              
257             The package from which the subtest should be invoked.
258             Typically, this would be the package that the subroutine lives in.
259             Calling the C<run()> method with an C<invocant> argument takes precedence over this.
260             It also appears in the fully qualified subroutine name, if C<run()> is called in verbose mode.
261             Defaults to C<main> if not given.
262              
263             =item sub_name
264              
265             The name of the subroutine to call for this subtest.
266             If C<coderef> is defined, this is only needed for display purposes.
267             If C<coderef> is not defined, the C<run()> method will attempt to find a sub with this name that can be called
268             via the C<invocant> or C<package> arguments.
269              
270             =back
271              
272             =head2 prepend
273              
274             subtests()->prepend( coderef => \%my_sub );
275              
276             Adds a subtest to the head of the current queue of subtests.
277             Takes the same arguments as the C<add()> method, and sets the C<where> param to C<prepend>.
278              
279             =head2 append
280              
281             subtests()->append( coderef => \%my_sub );
282              
283             Adds a subtest to the end of the current queue of subtests.
284             Takes the same arguments as the C<add()> method, and sets the C<where> param to C<append>.
285              
286             =head2 remove
287              
288             subtests()->remove( $name_or_coderef );
289              
290             Removes the indicated subtest(s) from the queue.
291             The argument can either be the name or the coderef associated with the subtest.
292              
293             =head2 get_all
294              
295             subtests()->get_all();
296              
297             Returns a list of all of the subtests currently in the queue.
298              
299             =head2 run
300              
301             subtests()->run( %args );
302              
303             Runs all of the subtests that are currently in the queue.
304              
305             This method can be called with any of the following arguments:
306              
307             =over
308              
309             =item builder
310              
311             The test builder to use. If none is given, a new L<Test::Builder> instance will be created.
312              
313             =item invocant
314              
315             If given, the subtest subroutines will be invoked via this reference.
316              
317             NOTE: When the C<:Subtest> attribute is used, the name of the package that the subroutine appears in will be remembered in the subtest
318             metadata, and that package name will be used if no C<invocant> argument is given explicitly when calling this method.
319             If that value happens to be undefined for any reason, the package name C<main> is the default instead.
320              
321             =item verbose_names
322              
323             When given, and set to a true value, subtest names will be displayed with C< [sub name]> appended.
324             If the package name can be determined, and is not C<main>, the sub name will be fully qualified with such.
325              
326             =back
327              
328             =head1 FUNCTIONS
329              
330             =head2 subtests
331              
332             Returns a handle that can be used to invoke the methods in this module.
333             As such, this is the only function exported by this module.
334              
335             Currently, this just returns the name of this package, but, in the future, it could return an object instance.
336              
337             =head1 SEE ALSO
338              
339             L<Attribute::Handlers>
340             L<Test::Builder>
341              
342             =head1 AUTHOR
343              
344             Ben Marcotte <bmarcotte NOSPAM cpan.org>
345              
346             =head1 COPYRIGHT AND LICENSE
347              
348             This software is copyright (c) 2017 by Ben Marcotte.
349              
350             This is free software; you can redistribute it and/or modify it under
351             the same terms as the Perl 5 programming language system itself.
352              
353             =cut