File Coverage

blib/lib/Test/MockPackages/Mock.pm
Criterion Covered Total %
statement 183 183 100.0
branch 54 54 100.0
condition 31 33 93.9
subroutine 33 33 100.0
pod 6 6 100.0
total 307 309 99.3


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