File Coverage

blib/lib/Test/Magic.pm
Criterion Covered Total %
statement 21 48 43.7
branch 1 20 5.0
condition 0 16 0.0
subroutine 7 14 50.0
pod 1 1 100.0
total 30 99 30.3


line stmt bran cond sub pod time code
1             package Test::Magic;
2 2     2   20808 use warnings;
  2         4  
  2         68  
3 2     2   13 use strict;
  2         4  
  2         70  
4 2     2   11 use Carp;
  2         8  
  2         137  
5 2     2   10 use Test::More;
  2         4  
  2         17  
6             our @ISA = 'Test::More';
7             our @EXPORT = ('test', @Test::More::EXPORT);
8             our $VERSION = '0.21';
9            
10             =head1 NAME
11            
12             Test::Magic - terse tests with useful error feedback
13            
14             =head1 VERSION
15            
16             Version 0.21
17            
18             =cut
19            
20             sub import {
21 1     1   11 require Exporter;
22 1         2 local $Test::Builder::Level
23             = $Test::Builder::Level + 1;
24 1 50       5 plan splice @_, 1, $#_ if @_ > 1;
25 1         2 goto &{Exporter->can('import')}
  1         154  
26             }
27            
28             my %invert = qw(
29             == != eq ne
30             < >= lt ge
31             > <= gt le
32             );
33             @invert{values %invert} = keys %invert;
34            
35             use overload fallback => 0, 'nomethod' => sub {
36 0     0   0 my ($self, $expect, $flip, $op) = @_;
37 0         0 my ($got, $invert) = @$self{qw/got invert/};
38            
39 0 0       0 croak 'is/isnt unsupported on rhs of operator' if $flip;
40 0 0 0     0 croak "unsupported op: $op" unless $invert{$op}
41             or $op eq '~~';
42 0         0 bless do {
43             ($op eq '~~' or
44             ($op =~ /[!=]=/ and ref $expect eq ref qr//))
45             ? sub {
46 0   0 0   0 ref or $_ = qr/$_/ for $expect;
47 0         0 @_ = ($got, $expect, $_[0]);
48 0 0 0     0 ($invert xor $op eq '!=')
49             ? goto &unlike
50             : goto &like
51             }
52             : ($op eq '==' and ref $expect)
53             ? do {
54 0 0       0 croak 'unable to invert is_deeply' if $invert;
55             sub {
56 0     0   0 @_ = ($got, $expect, $_[0]);
57 0         0 goto &is_deeply
58             }
59 0         0 }
60             : sub {
61 0 0   0   0 $op = $invert{$op} if $invert;
62 0         0 @_ = ($got, $op, $expect, $_[0]);
63 0         0 goto &cmp_ok
64             }
65 0 0 0     0 } => 'Test::Magic::Test'
    0 0        
66 2     2   3109 };
  2         968  
  2         22  
67            
68             sub test {
69 0     0 1   my $name = shift;
70 0 0         if (grep {ref ne 'Test::Magic::Test'} @_) {
  0            
71 0           croak "invalid arguments for test:\n".
72             " did you use parenthesis around your comparison?\n".
73             " good: is 1 == 1;\n".
74             " bad: is(1 == 1);\n"
75             }
76 0           local $Test::Builder::Level
77             = $Test::Builder::Level + 1;
78 0 0         if (@_ == 1) {
79 0           $_[0]($name)
80             } else {
81 0           my $num = 1;
82 0           $_->($name.' '.$num++) for @_
83             }
84             }
85 2     2   672 BEGIN {undef $_ for *is, *isnt}
86 0     0     sub is ($) {bless {got => $_[0]}}
87 0     0     sub isnt ($) {bless {got => $_[0], invert => 1}}
88            
89             =head1 SYNOPSIS
90            
91             use Test::Magic tests => 9;
92            
93             test 'numbers',
94             is 1 == 1,
95             is 1 > 2;
96            
97             test 'strings',
98             is 'asdf' eq 'asdf',
99             is 'asdf' gt 'asdf';
100            
101             test 'regex',
102             is 'abcd' == qr/bc/, # == is overloaded when rhs is a regex
103             is 'abcd' ~~ q/bc/, # ~~ can be used with a string rhs in perl 5.10+
104             is 'badc' ~~ q/bc/;
105            
106             test 'data structures',
107             is [1, 2, 3] == [1, 2, 3], # also overloaded when rhs is a reference
108             is {a => 1, b => 2} == {a => 1, b => 1};
109            
110             results in the following output:
111            
112             1..9
113             ok 1 - numbers 1
114             not ok 2 - numbers 2
115             # Failed test 'numbers 2'
116             # at example.t line 3.
117             # '1'
118             # >
119             # '2'
120             ok 3 - strings 1
121             not ok 4 - strings 2
122             # Failed test 'strings 2'
123             # at example.t line 7.
124             # 'asdf'
125             # gt
126             # 'asdf'
127             ok 5 - regex 1
128             ok 6 - regex 2
129             not ok 7 - regex 3
130             # Failed test 'regex 3'
131             # at example.t line 11.
132             # 'badc'
133             # doesn't match '(?-xism:bc)'
134             ok 8 - data structures 1
135             not ok 9 - data structures 2
136             # Failed test 'data structures 2'
137             # at example.t line 16.
138             # Structures begin differing at:
139             # $got->{b} = '2'
140             # $expected->{b} = '1'
141             # Looks like you failed 4 tests of 9.
142            
143             you get the output of L's C< cmp_ok >, C< like >, or C< is_deeply >
144             with a more natural syntax, and the test's name is moved before the test and is
145             numbered if you have more than one test.
146            
147             =head1 EXPORT
148            
149             C< test is isnt > and everything from L except C< is > and C< isnt >
150            
151             =head1 SUBROUTINES
152            
153             =over 4
154            
155             =item C< test NAME, LIST_OF_TESTS >
156            
157             C< test > runs a list of tests. if there is one test, C< NAME > is used
158             unchanged. otherwise, each test is sequentially numbered (C< NAME 1 >,
159             C< NAME 2 >, ...)
160            
161             =item C< is GOT OPERATOR EXPECTED >
162            
163             prepares a test for C< test >. do not use parenthesis with C< is >.
164             if you must, it needs to be written C< (is 1 == 1) > and never C< is(1 == 1) >
165            
166             =item C< isnt GOT OPERATOR EXPECTED >
167            
168             prepares a test for C< test > that expects to fail. do not use parenthesis with
169             C< isnt >. if you must, it needs to be written C< (isnt 1 == 1) > and never
170             C< isnt(1 == 1) >
171            
172             =back
173            
174             =head1 NOTES
175            
176             this module does B use source filtering. for those interested in how it
177             does work, the code:
178            
179             test 'my test',
180             is 1 == 1,
181             is 1 == 2;
182            
183             is parsed as follows:
184            
185             test( 'my test,
186             (is(1) == 1),
187             (is(1) == 2)
188             );
189            
190             the C< is > function binds tightly to its argument, making the parenthesis
191             unnecessary. it returns an overloaded object that then captures the comparison
192             operator and the rhs argument. the overloading operation returns a code
193             reference which expects to be passed its test name. the C< test > function does
194             just that. so ultimately, the code becomes something like this:
195            
196             Test::More::cmp_ok( 1, '==', 1, 'my test 1' );
197             Test::More::cmp_ok( 1, '==', 2, 'my test 2' );
198            
199             C< cmp_ok > is used for most comparisons, C< like > or C< unlike > for regex,
200             and C< is_deeply > when the operator is C< == > and the rhs (the expected value)
201             is a reference.
202            
203             if you need to do some setup before the test:
204            
205             test 'this test requires setup', do {
206             my $obj = Package->new();
207             ...
208             is ref $obj eq 'Package',
209             is $obj->value eq 'some value'
210             };
211            
212             =head1 AUTHOR
213            
214             Eric Strom, C<< >>
215            
216             =head1 BUGS
217            
218             Please report any bugs or feature requests to
219             C, or through the web interface at
220             L. I will be
221             notified, and then you'll automatically be notified of progress on your bug as
222             I make changes.
223            
224             =head1 ACKNOWLEDGEMENTS
225            
226             this module uses C< Test::More > internally
227            
228             =head1 LICENSE AND COPYRIGHT
229            
230             Copyright 2010 Eric Strom.
231            
232             This program is free software; you can redistribute it and/or modify it
233             under the terms of either: the GNU General Public License as published
234             by the Free Software Foundation; or the Artistic License.
235            
236             See http://dev.perl.org/licenses/ for more information.
237            
238             =cut
239            
240             'Test::Magic' if 'first require'