File Coverage

blib/lib/Mock/Sub.pm
Criterion Covered Total %
statement 58 58 100.0
branch 10 10 100.0
condition n/a
subroutine 11 13 84.6
pod 5 5 100.0
total 84 86 97.6


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