File Coverage

blib/lib/Mock/Sub.pm
Criterion Covered Total %
statement 62 62 100.0
branch 10 10 100.0
condition n/a
subroutine 12 14 85.7
pod 5 5 100.0
total 89 91 97.8


line stmt bran cond sub pod time code
1             package Mock::Sub;
2 18     18   353169 use 5.006;
  18         53  
3 18     18   70 use strict;
  18         24  
  18         307  
4 18     18   56 use warnings;
  18         32  
  18         455  
5              
6 18     18   58 use Carp qw(confess);
  18         30  
  18         1015  
7 18     18   6254 use Mock::Sub::Child;
  18         27  
  18         449  
8 18     18   74 use Scalar::Util qw(weaken);
  18         22  
  18         8323  
9              
10             our $VERSION = '1.07';
11              
12             my %opts;
13              
14             sub import {
15 18     18   133 my ($class, %args) = @_;
16 18         1196 %opts = %args;
17             }
18             sub new {
19 54     54 1 3804 my $self = bless {}, shift;
20 54         74 %{ $self } = @_;
  54         138  
21              
22 54         126 for (keys %opts){
23 1         3 $self->{$_} = $opts{$_};
24             }
25 54         88 return $self;
26             }
27             sub mock {
28 66     66 1 4311 my $self = shift;
29 66         271 my $sub = shift;
30              
31 66 100       170 if (ref($self) ne 'Mock::Sub'){
32 3         471 confess
33             "calling mock() on the Mock::Sub class is no longer permitted. " .
34             "create a new mock object with Mock::Sub->new;, then call mock " .
35             "with my \$sub_object = \$mock->mock('sub_name'); ";
36             }
37 63         95 my %p = @_;
38 63         115 for (keys %p){
39 19         34 $self->{$_} = $p{$_};
40             }
41              
42 63 100       139 if (! defined wantarray){
43 1         113 confess "\n\ncalling mock() in void context isn't allowed. ";
44             }
45              
46 62         280 my $child = Mock::Sub::Child->new(no_warnings => $self->{no_warnings});
47              
48 62         222 $child->side_effect($self->{side_effect});
49 60         161 $child->return_value($self->{return_value});
50              
51 60         135 $self->{objects}{$sub}{obj} = $child;
52 60         127 $child->_mock($sub);
53              
54             # remove the REFCNT to the child, or else DESTROY won't be called
55 60         124 weaken $self->{objects}{$sub}{obj};
56              
57 60         105 return $child;
58             }
59             sub mocked_subs {
60 3     3 1 9 my $self = shift;
61              
62 3         2 my @names;
63              
64 3         3 for (keys %{ $self->{objects} }) {
  3         8  
65 9 100       13 if ($self->mocked_state($_)){
66 8         9 push @names, $_;
67             }
68             }
69 3         8 return @names;
70             }
71             sub mocked_objects {
72 2     2 1 5 my $self = shift;
73              
74 2         2 my @mocked;
75 2         3 for (keys %{ $self->{objects} }){
  2         6  
76 6         8 push @mocked, $self->{objects}{$_}{obj};
77             }
78 2         7 return @mocked;
79             }
80             sub mocked_state {
81 13     13 1 16 my ($self, $sub) = @_;
82              
83 13 100       19 if (! $sub){
84 1         143 confess "calling mocked_state() on a Mock::Sub object requires a sub " .
85             "name to be passed in as its only parameter. ";
86             }
87              
88 12         10 eval {
89 12         30 my $test = $self->{objects}{$sub}{obj}->mocked_state();
90             };
91 12 100       20 if ($@){
92 1         150 confess "can't call mocked_state() on the class if the sub hasn't yet " .
93             "been mocked. ";
94             }
95 11         17 return $self->{objects}{$sub}{obj}->mocked_state;
96             }
97       0     sub DESTROY {
98             }
99       0     sub __end {}; # vim fold placeholder
100              
101             1;
102             =head1 NAME
103              
104             Mock::Sub - Mock package, object and standard subroutines, with unit testing in mind.
105              
106             =for html
107            
108             Coverage Status
109              
110             =head1 SYNOPSIS
111              
112             # see EXAMPLES for a full use case and caveats
113              
114             use Mock::Sub;
115              
116             # disable warnings about mocking non-existent subs
117              
118             use Mock::Sub no_warnings => 1
119              
120             # create the parent mock object
121              
122             my $mock = Mock::Sub->new;
123              
124             # mock some subs...
125              
126             my $foo = $mock->mock('Package::foo');
127             my $bar = $mock->mock('Package::bar');
128              
129             # wait until a mocked sub is called
130              
131             Package::foo();
132              
133             # then...
134              
135             $foo->name; # name of sub that's mocked
136             $foo->called; # was the sub called?
137             $foo->called_count; # how many times was it called?
138             $foo->called_with; # array of params sent to sub
139              
140             # have the mocked sub return something when it's called (list or scalar).
141              
142             $foo->return_value(1, 2, {a => 1});
143             my @return = Package::foo;
144              
145             # have the mocked sub perform an action
146              
147             $foo->side_effect( sub { die "eval catch" if @_; } );
148              
149             eval { Package::foo(1); };
150             like ($@, qr/eval catch/, "side_effect worked with params");
151              
152             # extract the parameters the sub was called with
153              
154             my @args = $foo->called_with;
155              
156             # reset the mock object for re-use within the same scope
157              
158             $foo->reset;
159              
160             # restore original functionality to the sub
161              
162             $foo->unmock;
163              
164             # re-mock a previously unmock()ed sub
165              
166             $foo->remock;
167              
168             # check if a sub is mocked
169              
170             my $state = $foo->mocked_state;
171              
172             # mock out a CORE:: function. Be warned that this *must* be done within
173             # compile stage (BEGIN), and the function can NOT be unmocked prior
174             # to the completion of program execution
175              
176             my ($mock, $caller);
177              
178             BEGIN {
179             $mock = Mock::Sub->new;
180             $caller = $mock->mock('caller');
181             };
182              
183             $caller->return_value(55);
184             caller(); # mocked caller() called
185              
186             =head1 DESCRIPTION
187              
188             Easy to use and very lightweight module for mocking out sub calls.
189             Very useful for testing areas of your own modules where getting coverage may
190             be difficult due to nothing to test against, and/or to reduce test run time by
191             eliminating the need to call subs that you really don't want or need to test.
192              
193             =head1 EXAMPLE
194              
195             Here's a full example to get further coverage where it's difficult if not
196             impossible to test certain areas of your code (eg: you have if/else statements,
197             but they don't do anything but call other subs. You don't want to test the
198             subs that are called, nor do you want to add statements to your code).
199              
200             Note that if the end subroutine you're testing is NOT Object Oriented (and
201             you're importing them into your module that you're testing), you have to mock
202             them as part of your own namespace (ie. instead of Other::first, you'd mock
203             MyModule::first).
204              
205             # module you're testing:
206              
207             package MyPackage;
208            
209             use Other;
210             use Exporter qw(import);
211             @EXPORT_OK = qw(test);
212            
213             my $other = Other->new;
214              
215             sub test {
216             my $arg = shift;
217            
218             if ($arg == 1){
219             # how do you test this?... there's no return etc.
220             $other->first();
221             }
222             if ($arg == 2){
223             $other->second();
224             }
225             }
226              
227             # your test file
228              
229             use MyPackage qw(test);
230             use Mock::Sub;
231             use Test::More tests => 2;
232              
233             my $mock = Mock::Sub->new;
234              
235             my $first = $mock->mock('Other::first');
236             my $second = $mock->mock('Other::second');
237              
238             # coverage for first if() in MyPackage::test
239             test(1);
240             is ($first->called, 1, "1st if() statement covered");
241              
242             # coverage for second if()
243             test(2);
244             is ($second->called, 1, "2nd if() statement covered");
245              
246             =head1 MOCK OBJECT METHODS
247              
248             =head2 C
249              
250             Instantiates and returns a new C object, ready to be used to start
251             creating mocked sub objects.
252              
253             Optional options:
254              
255             =over 4
256              
257             =item C $scalar>
258              
259             Set this to have all mocked subs created with this mock object return anything
260             you wish (accepts a single scalar only. See C method to return
261             a list and for further information). You can also set it in individual mocks
262             only (see C method).
263              
264             =item C $cref>
265              
266             Set this in C to have the side effect passed into all child mocks
267             created with this object. See C method.
268              
269             =back
270              
271             =head2 C
272              
273             Instantiates and returns a new mock object on each call. 'sub' is the name of
274             the subroutine to mock (requires full package name if the sub isn't in
275             C).
276              
277             The mocked sub will return undef if a return value isn't set, or a side effect
278             doesn't return anything.
279              
280             Optional parameters:
281              
282             See C for a description of the parameters. Both the C and
283             C parameters can be set in this method to individualize each mock
284             object, and will override the global configuration if set in C.
285              
286             There's also C and C methods if you want to
287             set, change or remove these values after instantiation of a child sub object.
288              
289             =head2 mocked_subs
290              
291             Returns a list of all the names of the subs that are currently mocked under
292             the parent mock object.
293              
294             =head2 mocked_objects
295              
296             Returns a list of all sub objects underneath the parent mock object, regardless
297             if its sub is currently mocked or not.
298              
299             =head2 mocked_state('Sub::Name')
300              
301             Returns 1 if the sub currently under the parent mock object is mocked or not,
302             and 0 if not. Croaks if there hasn't been a child sub object created with this
303             sub name.
304              
305             =head1 SUB OBJECT METHODS
306              
307             These methods are for the children mocked sub objects returned from the
308             parent mock object. See L for methods related
309             to the parent mock object.
310              
311             =head2 C
312              
313             Restores the original functionality back to the sub, and runs C on
314             the object.
315              
316             =head2 C
317              
318             Re-mocks the sub within the object after calling C on it (accepts the
319             side_effect and return_value parameters).
320              
321             =head2 C
322              
323             Returns true (1) if the sub being mocked has been called, and false (0) if not.
324              
325             =head2 C
326              
327             Returns the number of times the mocked sub has been called.
328              
329             =head2 C
330              
331             Returns an array of the parameters sent to the subroutine. C if
332             we're called before the mocked sub has been called.
333              
334             =head2 C
335              
336             Returns true (1) if the sub the object refers to is currently mocked, and
337             false (0) if not.
338              
339             =head2 C
340              
341             Returns the name of the sub being mocked.
342              
343             =head2 C
344              
345             Add (or change/delete) a side effect after instantiation.
346              
347             Send in a code reference containing an action you'd like the
348             mocked sub to perform.
349              
350             The side effect function will receive all parameters sent into the mocked sub.
351              
352             You can use both C and C params at the same
353             time. C will be run first, and then C. Note that if
354             C's last expression evaluates to any value whatsoever
355             (even false), it will return that and C will be skipped.
356              
357             To work around this and have the side_effect run but still get the
358             return_value thereafter, write your cref to evaluate undef as the last thing
359             it does: C.
360              
361             =head2 C
362              
363             Add (or change/delete) the mocked sub's return value after instantiation.
364             Can be a scalar or list. Send in C to remove previously set values.
365              
366             =head2 C
367              
368             Resets the functional parameters (C, C), along
369             with C and C back to undef/false. Does not restore
370             the sub back to its original state.
371              
372             =head1 NOTES
373              
374             This module has a backwards parent-child relationship. To use, you create a
375             mock object using L C and C methods,
376             thereafter, you use the returned mocked sub object L to perform the
377             work.
378              
379             The parent mock object retains certain information and statistics of the child
380             mocked objects (and the subs themselves).
381              
382             To mock CORE::GLOBAL functions, you *must* initiate within a C block
383             (see C for details). It is important that if you mock a CORE sub,
384             it can't and won't be returned to its original state until after the entire
385             program process tree exists. Period.
386              
387             I didn't make this a C module (although it started that way) because
388             I can see more uses than placing it into that category.
389              
390             =head1 AUTHOR
391              
392             Steve Bertrand, C<< >>
393              
394             =head1 BUGS
395              
396             Please report any bugs or requests at
397             L
398              
399             =head1 REPOSITORY
400              
401             L
402              
403             =head1 BUILD RESULTS
404              
405             CPAN Testers: L
406              
407             =head1 SUPPORT
408              
409             You can find documentation for this module with the perldoc command.
410              
411             perldoc Mock::Sub
412              
413             =head1 ACKNOWLEDGEMENTS
414              
415             Python's MagicMock module.
416              
417             =head1 LICENSE AND COPYRIGHT
418              
419             Copyright 2016 Steve Bertrand.
420              
421             This program is free software; you can redistribute it and/or modify it
422             under the terms of either: the GNU General Public License as published
423             by the Free Software Foundation; or the Artistic License.
424              
425             See L for more information.
426              
427              
428             =cut
429              
430             1; # End of Mock::Sub
431