File Coverage

blib/lib/Test/Mocha.pm
Criterion Covered Total %
statement 136 136 100.0
branch 30 30 100.0
condition 10 12 83.3
subroutine 42 42 100.0
pod 15 17 100.0
total 233 237 99.1


line stmt bran cond sub pod time code
1             package Test::Mocha;
2             # ABSTRACT: Test double framework with method stubs and behaviour verification
3             $Test::Mocha::VERSION = '0.67';
4              
5 13     13   1348128 use strict;
  13         158  
  13         370  
6 13     13   68 use warnings;
  13         26  
  13         391  
7              
8 13     13   67 use Carp 'croak';
  13         25  
  13         735  
9 13     13   84 use Exporter 'import';
  13         31  
  13         429  
10 13     13   81 use Scalar::Util 'blessed';
  13         45  
  13         643  
11 13     13   6029 use Test::Mocha::CalledOk::Times;
  13         33  
  13         380  
12 13     13   5637 use Test::Mocha::CalledOk::AtLeast;
  13         38  
  13         374  
13 13     13   5650 use Test::Mocha::CalledOk::AtMost;
  13         32  
  13         407  
14 13     13   5609 use Test::Mocha::CalledOk::Between;
  13         30  
  13         379  
15 13     13   5627 use Test::Mocha::Mock;
  13         49  
  13         531  
16 13     13   5975 use Test::Mocha::Spy;
  13         37  
  13         520  
17 13     13   97 use Test::Mocha::Types 'NumRange';
  13         75  
  13         102  
18 13     13   5442 use Test::Mocha::Util 'extract_method_name';
  13         26  
  13         723  
19 13     13   77 use Types::Standard qw( ArrayRef HashRef Num slurpy );
  13         27  
  13         110  
20              
21             our @EXPORT = qw(
22             mock
23             spy
24             class_mock
25             stub
26             returns
27             throws
28             executes
29             called_ok
30             times
31             atleast
32             atmost
33             between
34             verify
35             inspect
36             inspect_all
37             clear
38             SlurpyArray
39             SlurpyHash
40             );
41              
42             # croak() messages should not trace back to Mocha modules
43             $Carp::Internal{$_}++ foreach qw(
44             Test::Mocha
45             Test::Mocha::CalledOk
46             Test::Mocha::MethodStub
47             Test::Mocha::Mock
48             Test::Mocha::Spy
49             Test::Mocha::Util
50             );
51              
52             sub mock {
53 22     22 1 21803 return Test::Mocha::Mock->__new(@_);
54             }
55              
56             sub spy ($) {
57 8     8 1 2126 return Test::Mocha::Spy->__new(@_);
58             }
59              
60             sub stub (&@) {
61 67     67 1 25754 my ( $coderef, @responses ) = @_;
62              
63 67         136 foreach (@responses) {
64 66 100       444 croak 'stub() responses should be supplied using ',
65             'returns(), throws() or executes()'
66             if ref ne 'CODE';
67             }
68              
69 65         242 my @method_calls =
70             Test::Mocha::Mock->__capture_method_calls( $coderef, 'stub' );
71 57         119 for my $method_call (@method_calls) {
72             # add stub to mock
73 60         113 unshift @{ $method_call->invocant->__stubs->{ $method_call->name } },
  60         157  
74             $method_call;
75              
76             # add response to stub
77 60         249 Test::Mocha::MethodStub->cast($method_call);
78 60         79 push @{ $method_call->__responses }, @responses;
  60         133  
79             }
80 57         132 return;
81             }
82              
83             sub returns (@) {
84 42     42 1 59688 my (@return_values) = @_;
85 46     46   317 return sub { $return_values[0] }
86 42 100       266 if @return_values == 1;
87 4     4   28 return sub { @return_values }
88 4 100       25 if @return_values > 1;
89 2     4   11 return sub { }; # if @return_values == 0
90             }
91              
92             sub throws (@) {
93 18     18 1 39616 my (@exception) = @_;
94              
95             # check if first arg is a throwable exception
96 2     2   9 return sub { $exception[0]->throw }
97 18 100 100     140 if blessed( $exception[0] ) && $exception[0]->can('throw');
98              
99 16     16   147 return sub { croak @exception };
  16         1710  
100              
101             }
102              
103             sub executes (&) {
104 4     4 1 5166 my ($callback) = @_;
105 4         14 return $callback;
106             }
107              
108             ## no critic (RequireArgUnpacking,ProhibitMagicNumbers)
109             sub called_ok (&;@) {
110 121     121 1 72917 my $coderef = shift;
111              
112 121         218 my $called_ok;
113             my $test_name;
114 121 100 100     652 if ( @_ > 0 && ref $_[0] eq 'CODE' ) {
115 75         122 $called_ok = shift;
116             }
117 121 100       287 if ( @_ > 0 ) {
118 65         112 $test_name = shift;
119             }
120              
121 121         448 my @method_calls =
122             Test::Mocha::Mock->__capture_method_calls( $coderef, 'verify' );
123              
124             ## no critic (ProhibitAmpersandSigils)
125 114         220 local $Test::Builder::Level = $Test::Builder::Level + 1;
126 114   66     309 $called_ok ||= ×(1); # default if no times() is specified
127 114         363 $called_ok->( $_, $test_name ) for @method_calls;
128 114         589 return;
129             }
130             ## use critic
131              
132             ## no critic (ProhibitBuiltinHomonyms)
133             sub times ($) {
134 91     91 1 46271 my ($n) = @_;
135 91 100       288 croak 'times() must be given a number'
136             unless Num->check($n);
137              
138             return sub {
139 89     89   184 my ( $method_call, $test_name ) = @_;
140 89         343 Test::Mocha::CalledOk::Times->test( $method_call, $n, $test_name );
141 89         1759 };
142             }
143             ## use critic
144              
145             sub atleast ($) {
146 8     8 1 33310 my ($n) = @_;
147 8 100       32 croak 'atleast() must be given a number'
148             unless Num->check($n);
149              
150             return sub {
151 6     6   22 my ( $method_call, $test_name ) = @_;
152 6         34 Test::Mocha::CalledOk::AtLeast->test( $method_call, $n, $test_name );
153 6         147 };
154             }
155              
156             sub atmost ($) {
157 8     8 1 32784 my ($n) = @_;
158 8 100       31 croak 'atmost() must be given a number'
159             unless Num->check($n);
160              
161             return sub {
162 6     6   17 my ( $method_call, $test_name ) = @_;
163 6         33 Test::Mocha::CalledOk::AtMost->test( $method_call, $n, $test_name );
164 6         125 };
165             }
166              
167             sub between ($$) {
168 17     17 1 78707 my ( $lower, $upper ) = @_;
169 17 100       70 croak 'between() must be given 2 numbers in ascending order'
170             unless NumRange->check( [ $lower, $upper ] );
171              
172             return sub {
173 17     17   41 my ( $method_call, $test_name ) = @_;
174 17         77 Test::Mocha::CalledOk::Between->test( $method_call, [ $lower, $upper ],
175             $test_name );
176 13         166 };
177             }
178              
179             sub inspect (&) {
180 20     20 1 12703 my ($coderef) = @_;
181 20         88 my @method_calls =
182             Test::Mocha::Mock->__capture_method_calls( $coderef, 'inspect' );
183              
184 15         24 my @inspect;
185 15         31 foreach my $method_call (@method_calls) {
186             push @inspect,
187 61         133 grep { $method_call->__satisfied_by($_) }
188 15         26 @{ $method_call->invocant->__calls };
  15         39  
189             }
190 15         119 return @inspect;
191             }
192              
193             sub inspect_all ($) {
194 4     4 1 2909 my ($mock) = @_;
195              
196 4 100       280 croak 'inspect_all() must be given a mock or spy object'
197             if !$mock->isa('Test::Mocha::SpyBase');
198              
199 2         4 return @{ $mock->{calls} };
  2         7  
200             }
201              
202             sub clear (@) {
203 3     3 1 8535 my @mocks = @_;
204              
205 3 100       197 croak 'clear() must be given mock or spy objects'
206             if @mocks == 0;
207             croak 'clear() accepts mock and spy objects only'
208 2 100 66     5 if 0 < ( grep { !ref $_ || !$_->isa('Test::Mocha::SpyBase') } @mocks );
  3         129  
209              
210 1         4 @{ $_->__calls } = () foreach @mocks;
  2         5  
211              
212 1         4 return;
213             }
214              
215             ## no critic (NamingConventions::Capitalization)
216             sub SlurpyArray () {
217             # uncoverable pod
218 16     16 0 117 return slurpy(ArrayRef);
219             }
220              
221             sub SlurpyHash () {
222             # uncoverable pod
223 4     4 0 34 return slurpy(HashRef);
224             }
225             ## use critic
226              
227             sub class_mock {
228 3     3 1 594 my ($mocked_class) = @_;
229              
230 3         16 my $module_file = join( q{/}, split q{::}, $mocked_class ) . '.pm';
231 3         8 my $caller_pkg = caller;
232 13     13   29491 no strict 'refs'; ## no critic (TestingAndDebugging::ProhibitNoStrict)
  13         40  
  13         2664  
233              
234             # make sure the real module is not already loaded
235             croak "Package '$mocked_class' is already loaded so it cannot be mocked"
236 3 100       5 if defined ${ $caller_pkg . '::INC' }{$module_file};
  3         180  
237              
238             # check if package has already been mocked
239             croak "Package '$mocked_class' is already mocked"
240 2 100       3 if defined *{ $mocked_class . '::AUTOLOAD' }{CODE};
  2         96  
241              
242 1         4 my $mock = mock($mocked_class);
243              
244 1         5 *{ $mocked_class . '::AUTOLOAD' } = sub {
245 16     16   280 my ($method) = extract_method_name( our $AUTOLOAD );
246 16         86 $mock->$method(@_);
247 1         5 };
248 1         7 return $mock;
249             }
250              
251             1;
252              
253             __END__