File Coverage

blib/lib/Test/Wrapper.pm
Criterion Covered Total %
statement 2 4 50.0
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 4 6 66.6


line stmt bran cond sub pod time code
1             package Test::Wrapper;
2             BEGIN {
3 2     2   51972 $Test::Wrapper::AUTHORITY = 'cpan:YANICK';
4             }
5             $Test::Wrapper::VERSION = '0.3.0';
6             # ABSTRACT: Use Test::* tests outside of a TAP context
7              
8              
9 2     2   3331 use Moose;
  0            
  0            
10             use Moose::Exporter;
11              
12             use Test::Builder;
13              
14             no warnings qw/ uninitialized /; # I know, I'm a bad boy
15              
16             Moose::Exporter->setup_import_methods( as_is => ['test_wrap'] );
17              
18              
19             sub test_wrap {
20             my ( $test, %args ) = @_;
21              
22             my @tests = ref $test ? @$test : ($test);
23              
24             my $package = __PACKAGE__;
25             my $level = 1;
26              
27             ($package) = caller $level++ while $package eq __PACKAGE__;
28              
29             for my $t (@tests) {
30              
31             my $to_wrap = join '::', $package, $args{prefix} . $t;
32              
33             my $original = join '::', $package, $t;
34             my $original_ref = eval '\&' . $original;
35              
36             my $proto = prototype $original_ref;
37             $proto &&= "($proto)";
38              
39             no warnings qw/ redefine /;
40              
41             eval sprintf <<'END', $to_wrap, $proto;
42             sub %s %s {
43             Test::Wrapper->run_test( $t, $original_ref, @_ );
44             }
45             END
46              
47             die $@ if $@;
48             }
49             }
50              
51              
52             has [qw/ diag output todo /] => ( is => 'ro', );
53              
54             sub is_success {
55             return $_[0]->output =~ /^ok/;
56             }
57              
58             has "_test_args" => (
59             traits => [ 'Array' ],
60             isa => 'ArrayRef',
61             is => 'ro',
62             default => sub { [] },
63             handles => {
64             test_args => 'elements',
65             },
66             );
67              
68             has "test_name" => (
69             isa => 'Str',
70             is => 'ro',
71             );
72              
73              
74             sub BUILD {
75             my $self = shift;
76              
77             # we don't need the commenting
78             $self->{diag} =~ s/^\s*#//mg;
79             }
80              
81             sub run_test {
82             my( undef, $name, $original_ref, @args ) = @_;
83             $name =~ s/^:://;
84              
85             local $Test::Builder::Test = undef;
86              
87             my $builder = Test::Builder->new;
88              
89             $builder->{Have_Plan} = 1;
90             $builder->{Have_Output_Plan} = 1;
91             $builder->{Expected_Tests} = 1;
92              
93             $builder->{History} = Test::Builder2::History->create
94             if Test::More->VERSION >= 2;
95              
96             $builder->output( \my $output );
97             $builder->failure_output( \my $failure);
98             $builder->todo_output( \my $todo );
99              
100             $original_ref->( @args );
101              
102             return Test::Wrapper->new(
103             test_name => $name,
104             _test_args => \@args,
105             output => $output,
106             diag => $failure,
107             todo => $todo,
108             );
109              
110             }
111              
112              
113             use overload
114             'bool' => 'is_success',
115             '""' => sub { $_[0]->diag };
116              
117             __PACKAGE__->meta->make_immutable;
118              
119             1;
120              
121             __END__
122              
123             =pod
124              
125             =encoding UTF-8
126              
127             =head1 NAME
128              
129             Test::Wrapper - Use Test::* tests outside of a TAP context
130              
131             =head1 VERSION
132              
133             version 0.3.0
134              
135             =head1 SYNOPSIS
136              
137             use Test::Wrapper;
138             use Test::More;
139              
140             test_wrap( 'like' );
141              
142             # doesn't output anything
143             my $test = like 'foo' => qr/bar/;
144              
145             unless ( $test->is_success ) {
146             print "test failed, diag output is: ", $test->diag;
147             }
148              
149             =head1 DESCRIPTION
150              
151             This module for the occasions where a C<Test::*> test would
152             be perfect for what you want to do, but the module doesn't provide
153             an helper function that doesn't produce TAP.
154              
155             C<Test::Wrapper> exports a single function, C<test_wrap>, in the calling package,
156             which wraps the desired testing functions. After being wrapped, the test
157             functions will not emit TAP anymore, but rather return a C<Test::Wrapper>
158             object.
159              
160             It must be noted that C<Test::Wrapper> only works with test modules inheriting
161             from L<Test::Builder::Module>.
162              
163             Finally, C<Test::Wrapper> will not mess up the L<Test::Builder>, which means
164             that if you really want, you can use it within a test file. For example, this
165             would work:
166              
167             use strict;
168             use warnings;
169              
170             use Test::More tests => 1;
171              
172             use Test::Differences;
173             use Test::Wrapper;
174              
175             test_wrap( 'eq_or_diff' );
176              
177             my $test = eq_or_diff "foo", "bar";
178              
179             ok $test, "eq_or_diff passed" or diag $test->diag;
180              
181             =head1 EXPORTED METHOD
182              
183             =head2 test_wrap( $test | \@tests, %params )
184              
185             Wraps the given test or tests such that, when invoked, they will
186             not emit TAP output but return a C<Test::Wrapper> object.
187              
188             The parameters the function accepts are:
189              
190             =over
191              
192             =item prefix
193              
194             If defined, a wrapped function named '$prefix_<original_name>' will
195             be created, and the original test function will be left alone.
196              
197             use Test::More;
198             use Test::Wrapper;
199              
200             test_wrap( 'like', prefix => 'wrapped_' );
201              
202             like "foo" => qr/bar/; # will emit TAP
203              
204             # will not emit TAP
205             my $test = wrapped_like( "yadah" => qw/ya/ );
206              
207             Note that since the wrapped function will be created post-compile time,
208             its prototype will not be effective, so parenthesis have to be used.
209              
210             test_wrap( 'is' );
211             test_wrap( 'like', prefix => 'wrapped' );
212              
213             # prototype of the original function makes
214             # it magically work
215             my $t1 = is $foo => $bar;
216              
217             # this, alas, will break
218             my $t2 = like $foo => qr/$baz/;
219              
220             # ... so you have to do this instead
221             my $t2 = like( $foo => qr/$baz/ );
222              
223             =back
224              
225             =head1 Attributes
226              
227             =head2 diag
228              
229             Diagnostic message of the test. Will be empty if the test passed.
230             The leading '#' of each line of the raw TAP output are stripped down.
231              
232             =head2 is_success
233              
234             Is C<true> if the test passed, C<false> otherwise.
235              
236             =head2 todo
237              
238             TODO message of the test.
239              
240             =head2 output
241              
242             TAP result of the test '(I<ok 1 - yadah>').
243              
244             =head2 test_name
245              
246             Name of the wrapped test.
247              
248             =head2 test_args
249              
250             The list of arguments passed to the test.
251              
252             =head1 OVERLOADING
253              
254             =head2 Boolean context
255              
256             In a boolean context, the object will returns the value given by its
257             C<is_success> attribute.
258              
259             test_wrap( 'like' );
260              
261             my $test = like $foo => $bar;
262              
263             if ( $test ) {
264             ...
265             }
266              
267             =head2 Stringify
268              
269             If stringified, the object will return the content of its C<diag> attribute.
270              
271             print $test unless $test;
272              
273             # equivalent to
274            
275             unless ( $test->is_success ) {
276             print $test->diag;
277             }
278              
279             =head1 AUTHOR
280              
281             Yanick Champoux <yanick@cpan.org>
282              
283             =head1 COPYRIGHT AND LICENSE
284              
285             This software is copyright (c) 2010 by Yanick Champoux.
286              
287             This is free software; you can redistribute it and/or modify it under
288             the same terms as the Perl 5 programming language system itself.
289              
290             =cut