File Coverage

blib/lib/Test/Mocha.pm
Criterion Covered Total %
statement 167 169 98.8
branch 70 70 100.0
condition 8 9 88.8
subroutine 42 42 100.0
pod 14 17 100.0
total 301 307 99.0


line stmt bran cond sub pod time code
1 21     21   840649 use strict;
  21         36  
  21         657  
2 21     21   80 use warnings;
  21         28  
  21         888  
3              
4             package Test::Mocha;
5             # ABSTRACT: Test Spy/Stub Framework
6             $Test::Mocha::VERSION = '0.61';
7              
8 21     21   82 use Carp qw( croak );
  21         24  
  21         1009  
9 21     21   84 use Exporter qw( import );
  21         29  
  21         477  
10 21     21   78 use Scalar::Util qw( blessed );
  21         21  
  21         1032  
11 21     21   6671 use Test::Mocha::CalledOk::Times;
  21         39  
  21         595  
12 21     21   6649 use Test::Mocha::CalledOk::AtLeast;
  21         36  
  21         430  
13 21     21   6380 use Test::Mocha::CalledOk::AtMost;
  21         30  
  21         414  
14 21     21   6394 use Test::Mocha::CalledOk::Between;
  21         28  
  21         428  
15 21     21   6209 use Test::Mocha::Mock;
  21         43  
  21         677  
16 21     21   182 use Test::Mocha::Types 'NumRange', Mock => { -as => 'MockType' };
  21         24  
  21         291  
17             use Test::Mocha::Util
18 21     21   10613 qw( getattr get_method_call is_called extract_method_name );
  21         32  
  21         1413  
19 21     21   91 use Types::Standard qw( ArrayRef HashRef Num slurpy );
  21         23  
  21         161  
20              
21             our @EXPORT = qw(
22             mock
23             class_mock
24             stub
25             returns
26             throws
27             executes
28             called_ok
29             times
30             atleast
31             atmost
32             between
33             verify
34             inspect
35             inspect_all
36             clear
37             SlurpyArray
38             SlurpyHash
39             );
40              
41             # croak() messages should not trace back to Mocha modules
42             $Carp::Internal{$_}++ foreach qw(
43             Test::Mocha
44             Test::Mocha::CalledOk
45             Test::Mocha::Inspect
46             Test::Mocha::Mock
47             Test::Mocha::Util
48             Test::Mocha::MethodStub
49             Test::Mocha::Verify
50             );
51              
52             sub mock {
53 61     61 1 40916 return Test::Mocha::Mock->new(@_);
54             }
55              
56             sub stub (&@) {
57 73     73 1 22093 my ( $arg, @executions ) = @_;
58              
59 73         127 foreach (@executions) {
60 35 100       306 croak 'stub() responses should be supplied using ',
61             'returns(), throws() or executes()'
62             if ref ne 'CODE';
63             }
64              
65 72         93 $Test::Mocha::Mock::num_method_calls = 0;
66 72         166 my $method_call = get_method_call($arg);
67 64         126 my $stubs = getattr( $method_call->invocant, 'stubs' );
68 64         63 unshift @{ $stubs->{ $method_call->name } }, $method_call;
  64         119  
69              
70 64         179 Test::Mocha::MethodStub->cast($method_call);
71 64         50 push @{ getattr( $method_call, 'executions' ) }, @executions;
  64         100  
72 64         157 return $method_call; # for backwards compatibility
73             }
74              
75             sub returns (@) {
76 21     21 1 6594 my (@return_values) = @_;
77 37     37   186 return sub { $return_values[0] }
78 21 100       124 if @return_values == 1;
79 2     2   9 return sub { @return_values }
80 2 100       9 if @return_values > 1;
81 1     2   4 return sub { }; # if @return_values == 0
  2         9  
82             }
83              
84             sub throws (@) {
85 10     10 1 7145 my (@exception) = @_;
86              
87             # check if first arg is a throwable exception
88 1     1   3 return sub { $exception[0]->throw }
89 10 100 100     65 if blessed( $exception[0] ) && $exception[0]->can('throw');
90              
91 9     10   46 return sub { croak @exception };
  10         939  
92              
93             }
94              
95             sub executes (&) {
96 3     3 1 899 my ($callback) = @_;
97 3         10 return $callback;
98             }
99              
100             ## no critic (RequireArgUnpacking,ProhibitMagicNumbers)
101             sub called_ok (&;@) {
102 182     182 1 32504 my $coderef = shift;
103 182         185 my $called_ok;
104             my $class;
105 0         0 my $value;
106 0         0 my $test_name;
107              
108             # unpack the args - different possibilities due to backwards compatibility
109 182 100       496 if ( @_ == 1 ) {
    100          
    100          
110 56 100       135 if ( ref $_[0] eq 'CODE' ) {
111 20         24 $called_ok = $_[0];
112             }
113             else {
114 36         54 $test_name = $_[0];
115             }
116             }
117             elsif ( @_ == 2 ) {
118 63 100       126 if ( ref $_[0] eq 'CODE' ) {
119 35         58 ( $called_ok, $test_name ) = @_;
120             }
121             else {
122 28         40 ( $class, $value ) = @_;
123             }
124             }
125             elsif ( @_ == 3 ) {
126 35         59 ( $class, $value, $test_name ) = @_;
127             }
128              
129 182         187 $Test::Mocha::Mock::num_method_calls = 0;
130 182         351 my $method_call = get_method_call($coderef);
131              
132             # v0.50 behaviour
133 176 100       262 if ( defined $class ) {
134 63         198 my %options = (
135             times => 'Test::Mocha::CalledOk::Times',
136             atleast => 'Test::Mocha::CalledOk::AtLeast',
137             atmost => 'Test::Mocha::CalledOk::AtMost',
138             at_least => 'Test::Mocha::CalledOk::AtLeast',
139             at_most => 'Test::Mocha::CalledOk::AtMost',
140             between => 'Test::Mocha::CalledOk::Between',
141             );
142 63 100       240 croak "called_ok() was given an invalid option: '$class'"
143             unless defined $options{$class};
144              
145 62 100       90 if ( $class ne 'between' ) {
146 53 100       98 croak "'$class' option must be a number"
147             unless Num->check($value);
148             }
149             else {
150 9 100       19 croak "'$class' option must be an arrayref "
151             . 'with 2 numbers in ascending order'
152             unless NumRange->check($value);
153             }
154              
155 55         616 $options{$class}->test( $method_call, $value, $test_name );
156 55         166 return;
157             }
158              
159             # current behaviour
160             ## no critic (ProhibitAmpersandSigils)
161 113         143 local $Test::Builder::Level = $Test::Builder::Level + 1;
162 113   66     249 $called_ok ||= ×(1); # default if no times() is specified
163 113         173 $called_ok->( $method_call, $test_name );
164 113         338 return;
165             }
166             ## use critic
167              
168             ## no critic (ProhibitBuiltinHomonyms)
169             sub times ($) {
170 103     103 1 3460 my ($n) = @_;
171 103 100       221 croak 'times() must be given a number'
172             unless Num->check($n);
173              
174             return sub {
175 102     102   107 my ( $method_call, $test_name ) = @_;
176 102         332 Test::Mocha::CalledOk::Times->test( $method_call, $n, $test_name );
177 102         1387 };
178             }
179             ## use critic
180              
181             sub atleast ($) {
182 4     4 1 2120 my ($n) = @_;
183 4 100       14 croak 'atleast() must be given a number'
184             unless Num->check($n);
185              
186             return sub {
187 3     3   6 my ( $method_call, $test_name ) = @_;
188 3         23 Test::Mocha::CalledOk::AtLeast->test( $method_call, $n, $test_name );
189 3         61 };
190             }
191              
192             sub atmost ($) {
193 4     4 1 2029 my ($n) = @_;
194 4 100       14 croak 'atmost() must be given a number'
195             unless Num->check($n);
196              
197             return sub {
198 3     3   4 my ( $method_call, $test_name ) = @_;
199 3         20 Test::Mocha::CalledOk::AtMost->test( $method_call, $n, $test_name );
200 3         60 };
201             }
202              
203             sub between ($$) {
204 7     7 1 3880 my ( $lower, $upper ) = @_;
205 7 100       21 croak 'between() must be given 2 numbers in ascending order'
206             unless NumRange->check( [ $lower, $upper ] );
207              
208             return sub {
209 5     5   9 my ( $method_call, $test_name ) = @_;
210 5         30 Test::Mocha::CalledOk::Between->test( $method_call, [ $lower, $upper ],
211             $test_name );
212 5         59 };
213             }
214              
215             # verify() has been retained for backwards compatibility only
216              
217             sub verify ($;@) {
218             # uncoverable pod
219 53     53 0 29077 my ( $mock, %options ) = _get_called_ok_args(@_);
220              
221 44         2816 warnings::warnif( 'deprecated',
222             'verify() is deprecated; use called_ok() instead' );
223 44 100       151 croak 'verify() must be given a mock object'
224             if !MockType->check($mock);
225              
226 43         1985 require Test::Mocha::Verify;
227 43         193 return Test::Mocha::Verify->new( mock => $mock, %options );
228             }
229              
230             ## no critic (RequireArgUnpacking)
231             sub _get_called_ok_args {
232 53     53   74 my $coderef = shift;
233 53         49 my $test_name;
234 53 100       146 $test_name = pop if ( @_ % 2 == 1 );
235 53         107 my %options = @_;
236              
237             # set default option if none given
238 53 100       131 $options{times} = 1 if keys %options == 0;
239              
240 2         85 croak 'You can set only one of these options: ' . join ', ',
241 53 100       107 map { "'$_'" } keys %options
242             unless keys %options == 1;
243              
244             ## no critic (ProhibitCascadingIfElse)
245 52 100       198 if ( defined $options{times} ) {
    100          
    100          
    100          
246 34 100       100 croak "'times' option must be a number"
247             unless Num->check( $options{times} );
248             }
249             elsif ( defined $options{at_least} ) {
250 4 100       12 croak "'at_least' option must be a number"
251             unless Num->check( $options{at_least} );
252             }
253             elsif ( defined $options{at_most} ) {
254 4 100       10 croak "'at_most' option must be a number"
255             unless Num->check( $options{at_most} );
256             }
257             elsif ( defined $options{between} ) {
258 9 100       23 croak "'between' option must be an arrayref "
259             . 'with 2 numbers in ascending order'
260             unless NumRange->check( $options{between} );
261             }
262             else {
263 1         3 my ($option) = keys %options;
264 1         55 croak "called_ok() was given an invalid option: '$option'";
265             }
266 44 100       666 $options{test_name} = $test_name if defined $test_name;
267              
268 44         162 return ( $coderef, %options );
269             }
270             ## use critic
271              
272             sub inspect (&) {
273 13     13 1 4010 my ($arg) = @_;
274              
275 13         20 $Test::Mocha::Mock::num_method_calls = 0;
276 13         42 my $method_call = get_method_call($arg);
277 9         25 my $mock = $method_call->invocant;
278 9         22 my $calls = getattr( $mock, 'calls' );
279 9         13 return grep { $method_call->satisfied_by($_) } @{$calls};
  54         117  
  9         16  
280             }
281              
282             sub inspect_all ($) {
283 4     4 1 2638 my ($mock) = @_;
284              
285 4 100       14 croak 'inspect_all() must be given a mock object'
286             if !MockType->check($mock);
287              
288 2         4 return @{ $mock->{calls} };
  2         7  
289             }
290              
291             sub clear (@) {
292 6     6 1 3014 my @mocks = @_;
293              
294             ## no critic (ProhibitBooleanGrep)
295 6         18 croak 'clear() must be given mock objects only'
296 6 100 100     248 if !@mocks || grep { !MockType->check($_) } @mocks;
297             ## use critic
298              
299 2         5 @{ getattr( $_, 'calls' ) } = () foreach @mocks;
  4         8  
300              
301 2         5 return;
302             }
303              
304             ## no critic (NamingConventions::Capitalization)
305             sub SlurpyArray () {
306             # uncoverable pod
307 24     24 0 128 return slurpy(ArrayRef);
308             }
309              
310             sub SlurpyHash () {
311             # uncoverable pod
312 8     8 0 62 return slurpy(HashRef);
313             }
314             ## use critic
315              
316             sub class_mock {
317 3     3 1 387 my ($mocked_class) = @_;
318              
319 3         13 my $module_file = join( q{/}, split q{::}, $mocked_class ) . '.pm';
320 3         5 my $caller_pkg = caller;
321 21     21   36461 no strict 'refs'; ## no critic (TestingAndDebugging::ProhibitNoStrict)
  21         35  
  21         2657  
322              
323             # make sure the real module is not already loaded
324 3         150 croak "Package '$mocked_class' is already loaded so it cannot be mocked"
325 3 100       3 if defined ${ $caller_pkg . '::INC' }{$module_file};
326              
327             # check if package has already been mocked
328 2         77 croak "Package '$mocked_class' is already mocked"
329 2 100       2 if defined *{ $mocked_class . '::AUTOLOAD' }{CODE};
330              
331 1         5 my $mock = mock($mocked_class);
332              
333 1         3 *{ $mocked_class . '::AUTOLOAD' } = sub {
334 14     14   112 my ($method) = extract_method_name( our $AUTOLOAD );
335 14         51 $mock->$method(@_);
336 1         5 };
337 1         7 return $mock;
338             }
339              
340             1;
341              
342             __END__