File Coverage

blib/lib/Test/MockPackages/Mock.pm
Criterion Covered Total %
statement 176 176 100.0
branch 52 52 100.0
condition 31 33 93.9
subroutine 32 32 100.0
pod 6 6 100.0
total 297 299 99.3


line stmt bran cond sub pod time code
1             package Test::MockPackages::Mock;
2 6     6   51230 use strict;
  6         7  
  6         125  
3 6     6   19 use warnings;
  6         6  
  6         116  
4 6     6   531 use utf8;
  6         12  
  6         20  
5              
6             our $VERSION = '0.9';
7              
8             =head1 NAME
9              
10             Test::MockPackages::Mock - handles mocking of individual methods and subroutines.
11              
12             =head1 VERSION
13              
14             Version 0.9
15              
16             =head1 SYNOPSIS
17              
18             my $m = Test::MockPackages::Mock->new( $package, $subroutine )
19             ->is_method()
20             ->expects( $arg1, $arg2 )
21             ->returns( 'ok' );
22              
23             =head1 DESCRIPTION
24              
25             Test::MockPackages::Mock will mock an individual subroutine or method on a given package. You most likely won't initialize new C objects directly, instead you
26             should have L create them for you using the C method.
27              
28             In short this package will allow you to verify that a given subroutine/method is: 1) called the correct number of times (see C, C, and C), 2) called with the correct arguments (see C), and 3) returns values you define (C).
29              
30             =head2 Examples
31              
32             Here's a trivial example. We have a subroutine, C that uses an external dependency, C to help calculate our value.
33              
34             sub calculate {
35             my ( $input ) = @ARG;
36              
37             return ACME::Widget::do_something( $input, 'CONSTANT' );
38             }
39              
40             When we test our C subroutine, we can mock the C call:
41              
42             subtest 'calculate()' => sub {
43             my $m = Test::MockPackages->new();
44             $m->pkg('ACME::Widget')
45             ->mock('do_something')
46             ->expects( 15, 'CONSTANT' )
47             ->returns( 20 );
48              
49             is( calculate( 15 ), 20, 'correct value returned from calculate' );
50             };
51              
52             The test output will look something like:
53              
54             ok 1 - ACME::Widget::do_something expects is correct
55             ok 2 - correct value returned from calculate
56             ok 3 - ACME::Widget::do_something called 1 time
57              
58             =cut
59              
60 6     6   224 use Carp qw(croak);
  6         6  
  6         230  
61 6     6   1949 use Const::Fast qw(const);
  6         9920  
  6         30  
62 6     6   394 use English qw(-no_match_vars);
  6         6  
  6         28  
63 6     6   1742 use Exporter qw(import);
  6         7  
  6         148  
64 6     6   3745 use Lingua::EN::Inflect qw(PL);
  6         93022  
  6         707  
65 6     6   55 use List::Util qw(max);
  6         6  
  6         483  
66 6     6   35 use Scalar::Util qw(looks_like_number weaken);
  6         8  
  6         286  
67 6     6   3923 use Storable qw(dclone);
  6         14219  
  6         361  
68 6     6   2731 use Test::Deep qw(cmp_deeply);
  6         36806  
  6         48  
69 6     6   1482 use Test::More;
  6         4334  
  6         61  
70 6     6   3929 use parent qw(Test::Builder::Module);
  6         1439  
  6         29  
71              
72             my $CLASS = __PACKAGE__;
73              
74             const my @GLOB_TYPES => qw(SCALAR HASH ARRAY HANDLE FORMAT IO);
75              
76             =head1 CONSTRUCTORS
77              
78             =head2 new( Str $package_name, Str $name )
79              
80             Instantiates a new Test::MockPackage::Mock object. C<$name> is the subroutine or method that you intend to mock in the named C<$package_name>.
81              
82             =cut
83              
84             sub new {
85 52     52 1 108649 my ( $pkg, $package_name, $name ) = @ARG;
86              
87 52         125 my $full_name = "${package_name}::$name";
88 52 100       180 my $original = exists &$full_name ? \&$full_name : undef;
89              
90 52         356 my $self = bless {
91             _allow_eval => 0,
92             _called => undef,
93             _expects => undef,
94             _full_name => $full_name,
95             _invoke_count => 0,
96             _is_method => 0,
97             _name => $name,
98             _never => 0,
99             _original_coderef => $original,
100             _package_name => $package_name,
101             _returns => undef,
102             _corrupt => 0,
103             }, $pkg;
104              
105 52         105 $self->_initialize();
106              
107 52         151 return $self;
108             }
109              
110             =head1 METHODS
111              
112             =head2 called( Int $called ) : Test::MockPackage::Mock, Throws '...'
113              
114             Will ensure that the subroutine/method has been called C<$called> times. This method cannot be used in combination with C.
115              
116             Setting C<$called> to C<-1> will prevent invocation count checks.
117              
118             You can combined this method with C and/or C to support repeated values. For example:
119              
120             $m->expects($arg1, $arg2)
121             ->expects($arg1, $arg2)
122             ->expects($arg1, $arg2)
123             ->expects($arg1, $arg2)
124             ->expects($arg1, $arg2);
125              
126             can be simplified as:
127              
128             $m->expects($arg1, $arg2)
129             ->called(5);
130              
131             By default, this package will ensure that a mocked subroutine/method is called the same number of times that C and/or C has been setup for. For example, if you call C three times, then when this object is destroyed we will ensure the mocked subroutine/method was called exactly three times, no more, no less.
132              
133             Therefore, you only need to use this method if you don't setup any expects or returns, or to simplify repeated values like what was shown up above.
134              
135             Return value: Returns itself to support the fluent interface.
136              
137             =cut
138              
139             sub called {
140 17     17 1 32 my ( $self, $called ) = @ARG;
141              
142 17 100 100     102 if ( !looks_like_number( $called ) || $called < -1 ) {
143 2         33 croak( '$called must be an integer >= -1' );
144             }
145              
146 15         19 $self->{_called} = $called;
147              
148 15         27 return $self->_validate();
149             }
150              
151             =head2 never_called() : Test::MockPackage::Mock, Throws '...'
152              
153             Ensures that this subroutine/method will never be called. This method cannot be used in combination with C, C, or C.
154              
155             Return value: Returns itself to support the fluent interface.
156              
157             =cut
158              
159             sub never_called {
160 7     7 1 15 my ( $self ) = @ARG;
161              
162 7         9 $self->{_never} = 1;
163              
164 7         13 return $self->_validate();
165             }
166              
167             =head2 is_method() : Test::MockPackage::Mock, Throws '...'
168              
169             Specifies that the mocked subroutine is a method. When setting up expectations using C, it will ignore the first value which is typically the object.
170              
171             Return value: Returns itself to support the fluent interface.
172              
173             =cut
174              
175             sub is_method {
176 4     4 1 11 my ( $self ) = @ARG;
177              
178 4         6 $self->{_is_method} = 1;
179              
180 4         10 return $self->_validate();
181             }
182              
183             =head2 expects( Any @expects ) : Test::MockPackage::Mock, Throws '...'
184              
185             Ensures that each invocation has the correct arguments passed in. If the subroutine/method will be called multiple times, you can call C multiple times. If
186             the same arguments are expected repeatedly, you can use this in conjunction with C. See L for further information.
187              
188             If you are mocking a method, be sure to call C at some point.
189              
190             When the C object goes out of scope, we'll test to make sure that the subroutine/method was called the correct number of times based on the number
191             of times that C was called, unless C is specified.
192              
193             The actual comparison is done using Test::Deep::cmp_deeply(), so you can use any of the associated helper methods to do a comparison.
194              
195             use Test::Deep qw(re);
196              
197             $m->mock( 'my_sub' )
198             ->expects( re( qr/^\d{5}\z/ ) );
199              
200             Return value: Returns itself to support the fluent interface.
201              
202             =cut
203              
204             sub expects {
205 29     29 1 1825 my ( $self, @expects ) = @ARG;
206              
207 29         41 push @{ $self->{_expects} }, \@expects;
  29         54  
208              
209 29         43 return $self->_validate();
210             }
211              
212             =head2 returns( Any @returns ) : Test::MockPackage::Mock, Throws '...'
213              
214             This method sets up what the return values should be. If the return values will change with each invocation, you can call this method multiple times.
215             If this method will always return the same values, you can call C once, and then pass in an appropriate value to C.
216              
217             When the C object goes out of scope, we'll test to make sure that the subroutine/method was called the correct number of times based on the number
218             of times that C was called, unless C is specified.
219              
220             Values passed in will be returned verbatim. A deep clone is also performed to accidental side effects aren't tested. If you don't want to have your data deep cloned, you can use returns_code.
221              
222             $m->mock('my_sub')
223             ->returns( $data_structure ); # $data_structure will be deep cloned using Storable::dclone();
224              
225             $m->mock('my_sub')
226             ->returns( returns_code { $data_structure } ); # $data_structure will not be cloned.
227              
228             If you plan on returning a L object, you will want to ensure that it's not deep cloned (using returns_code) because that module uses the object's address to keep track of mocked methods (instead of using attributes).
229              
230             C will be used to try and determine if a list or a single value should be returned. If C<@returns> contains a single element and C is false, the value at index 0 will be returned. Otherwise,
231             a list will be returned.
232              
233             If you'd rather have the value of a custom CODE block returned, you can pass in a CodeRef wrapped using a returns_code from the L package.
234              
235             use Test::MockPackages::Returns qw(returns_code);
236             ...
237             $m->expects( $arg1, $arg2 )
238             ->returns( returns_code {
239             my (@args) = @ARG;
240              
241             return join ', ', @args;
242             } );
243              
244             Return value: Returns itself to support the fluent interface.
245              
246             =cut
247              
248             sub returns {
249 29     29 1 61 my ( $self, @returns ) = @ARG;
250              
251             # dclone will remove the bless on the CodeRef.
252 29 100 66     114 if (@returns == 1 && do {
253 23         51 local $EVAL_ERROR = undef;
254 23         27 eval { $returns[ 0 ]->isa( 'Test::MockPackages::Returns' ) };
  23         183  
255             }
256             )
257             {
258 1         1 push @{ $self->{_returns} }, \@returns;
  1         3  
259             }
260             else {
261             # this should be safe since we are just doing a dclone(). According to the Storable POD, the eval is only dangerous
262             # when the input may contain malicious data (i.e. the frozen binary data).
263 28         31 local $Storable::Deparse = 1; ## no critic (Variables::ProhibitPackageVars)
264 28         20 local $Storable::Eval = 1; ## no critic (Variables::ProhibitPackageVars)
265              
266 28     1   25 push @{ $self->{_returns} }, dclone( \@returns );
  28     1   671  
  1     1   7  
  1         28  
  1         1009  
  1         4  
  1         1  
  1         29  
  1         4  
  1         1  
  1         19  
267             }
268              
269 29         68 return $self->_validate();
270             }
271              
272             # ----
273             # private methods
274             # ----
275              
276             # _initialize( ) : Bool
277             #
278             # This is where everythign is setup. We override the subroutine/method being mocked and replace it with a CodeRef
279             # that will perform the various expects checking and return values based on how returns were setup.
280             #
281             # Return value: True
282              
283             sub _initialize {
284 52     52   70 my ( $self ) = @ARG;
285              
286 52         135 my $test = $CLASS->builder;
287              
288 52         275 weaken $self;
289             my $mock = sub {
290 56     56   1384 my ( @got ) = @ARG;
291              
292             # _invoke_count keeps track of how many times this subroutine/method was called
293 56         66 my $invoke_number = ++$self->{_invoke_count};
294              
295             # $i is the current invocation
296 56         60 my $i = $invoke_number - 1;
297              
298             # The first value passed into the method is the object itself. Ignore that.
299 56 100       100 if ( $self->{_is_method} ) {
300 5         6 shift @got;
301             }
302              
303             # setup the expectations
304 56 100       110 if ( my $expects = $self->{_expects} ) {
305 27         36 my $n_expects = scalar( @$expects );
306 27         16 my $expected;
307 27 100 100     103 if ( $n_expects == 1 && defined( $self->{_called} ) ) {
    100          
308 6         12 $expected = $expects->[ 0 ];
309             }
310             elsif ( $i >= $n_expects ) {
311             croak(
312             sprintf(
313             '%s was called %d %s. Only %d %s defined',
314 1         4 $self->{_full_name}, $invoke_number, PL( 'time', $invoke_number ),
315             $n_expects, PL( 'expectation', $n_expects )
316             )
317             );
318             }
319             else {
320 20         23 $expected = $expects->[ $i ];
321             }
322              
323 26         25 local $Test::Builder::Level = $Test::Builder::Level + 1; ## no critic (Variables::ProhibitPackageVars)
324 26         102 cmp_deeply( \@got, $expected, "$self->{_full_name} expects is correct" );
325             }
326              
327             # setup the return values
328 55         43031 my @returns;
329 55 100       105 if ( my $returns = $self->{_returns} ) {
330 29         34 my $n_returns = scalar @$returns;
331              
332 29 100 100     122 if ( $n_returns == 1 && defined( $self->{_called} ) ) {
    100          
333 9         7 @returns = @{ $returns->[ 0 ] };
  9         20  
334             }
335             elsif ( $i >= $n_returns ) {
336             croak(
337             sprintf(
338             '%s was called %d %s. Only %d %s defined',
339 2         33 $self->{_full_name}, $invoke_number, PL( 'time', $invoke_number ),
340             $n_returns, PL( 'return', $n_returns )
341             )
342             );
343             }
344             else {
345 18         18 @returns = @{ $returns->[ $i ] };
  18         39  
346             }
347             }
348             else {
349 26         72 return;
350             }
351              
352 27 100 66     65 if (@returns == 1 && do {
353 21         20 local $EVAL_ERROR = undef;
354 21         26 eval { $returns[ 0 ]->isa( 'Test::MockPackages::Returns' ) };
  21         153  
355             }
356             )
357             {
358 1         5 return $returns[ 0 ]->( @got );
359             }
360              
361             # return the first element if only one return defined and a wantarray is false.
362 26 100 100     156 return !wantarray && scalar( @returns ) == 1 ? $returns[ 0 ] : @returns;
363 52         243 };
364              
365 52         55 do {
366 6     6   4532 no strict qw(refs); ## no critic (TestingAndDebugging::ProhibitNoStrict)
  6         14  
  6         172  
367 6     6   23 no warnings qw(redefine); ## no critic (TestingAndDebugging::ProhibitNoWarnings)
  6         6  
  6         1726  
368 52         75 my $full_name = $self->{_full_name};
369 52         155 *$full_name = $mock;
370             };
371              
372 52         54 return 1;
373             }
374              
375             # _validate( ) Test::MockPackages::Mock, Throws '...'
376             #
377             # Validates that the mock has been properly configured up to this point. If any errors
378             # were detected, raise an exception.
379             #
380             # Return value: Returns itself to support the fluent interface.
381              
382             sub _validate {
383 84     84   94 my ( $self ) = @ARG;
384              
385 84         95 my $called = $self->{_called};
386 84         80 my $never = $self->{_never};
387 84 100       109 my $n_expects = $self->{_expects} ? @{ $self->{_expects} } : 0;
  37         38  
388 84 100       108 my $n_returns = $self->{_returns} ? @{ $self->{_returns} } : 0;
  34         38  
389              
390             # called of -1 will be allowed with multiple expects and/or returns. Any other value of called will require that expects or returns
391             # has only been defined 0 or 1 time.
392 84 100 100     213 if ( defined( $called ) && $called >= 0 ) {
393              
394             # breaking into two if statements so Devel::Cover marks this condition as covered
395 22 100 100     83 if ( $n_expects > 1 || $n_returns > 1 ) {
396 2         3 $self->{_corrupt} = 1;
397 2         27 croak( 'called() cannot be used if expects() or returns() have been defined more than once' );
398             }
399             }
400              
401 82 100       120 if ( $never ) {
402              
403             # breaking into two if statements so Devel::Cover marks this condition as covered
404 10 100 100     55 if ( $called || $n_expects || $n_returns ) {
      100        
405 3         3 $self->{_corrupt} = 1;
406 3         28 croak( 'never_called() cannot be used if called(), expects(), or returns() have been defined' );
407             }
408             }
409              
410 79         205 return $self;
411             }
412              
413             # _expected_invocations( ) : Maybe[Int]
414             #
415             # Calculates how many times a subroutine/method is expected to be called.
416             #
417             # Return value: an integer value on the number of times the subroutine/method should be called.
418              
419             sub _expected_invocations {
420 52     52   55 my ( $self ) = @ARG;
421              
422 52 100       106 return 0 if $self->{_never};
423              
424 45 100       92 if ( defined( my $called = $self->{_called} ) ) {
425 14 100       27 if ( $called == -1 ) {
426 1         3 return;
427             }
428              
429 13         21 return $called;
430             }
431              
432 31 100       55 my $n_expects = $self->{_expects} ? @{ $self->{_expects} } : 0;
  14         18  
433 31 100       59 my $n_returns = $self->{_returns} ? @{ $self->{_returns} } : 0;
  16         20  
434 31         69 my $max = max( $n_expects, $n_returns );
435              
436 31 100       66 return $max >= 1 ? $max : undef;
437             }
438              
439             # DESTROY( )
440             #
441             # DESTROY is used to the original subroutine/method back into place and perform any final expectation checking.
442              
443             sub DESTROY {
444 6     6   24 no strict qw(refs); ## no critic (TestingAndDebugging)
  6         6  
  6         160  
445 6     6   20 no warnings qw(redefine); ## no critic (TestingAndDebugging)
  6         8  
  6         1106  
446              
447 52     52   7406 my ( $self ) = @ARG;
448              
449 52         74 my $full_name = $self->{_full_name};
450              
451 52         134 my $expected_invocations = $self->_expected_invocations;
452 52 100 100     248 if ( !$self->{_corrupt} && defined $expected_invocations ) {
453 40         52 local $Test::Builder::Level = $Test::Builder::Level + 6; ## no critic (Variables::ProhibitPackageVars)
454             $CLASS->builder->is_num( $self->{_invoke_count},
455 40         110 $expected_invocations,
456             sprintf( '%s called %d %s', $full_name, $expected_invocations, PL( 'time', $expected_invocations ) ) );
457             }
458              
459             # if we have an original CodeRef, put it back in place.
460 52 100       24345 if ( my $original = $self->{_original_coderef} ) {
461 42         465 *$full_name = $original;
462             }
463              
464             # otherwise, remove the CodeRef from the symbol table, but make sure the other types are
465             # left intact.
466             else {
467 10         13 my %copy;
468 10         18 $copy{$ARG} = *$full_name{$ARG} for grep { defined *$full_name{$ARG} } @GLOB_TYPES;
  60         137  
469 10         112 undef *$full_name;
470 10         46 *$full_name = $copy{$ARG} for keys %copy;
471             }
472              
473 52         331 return;
474             }
475              
476             1;
477              
478             __END__