File Coverage

blib/lib/Test/MockModule.pm
Criterion Covered Total %
statement 144 145 99.3
branch 50 54 92.5
condition 18 18 100.0
subroutine 28 29 96.5
pod 12 12 100.0
total 252 258 97.6


line stmt bran cond sub pod time code
1             package Test::MockModule;
2 8     8   509681 use warnings;
  8         55  
  8         293  
3 8     8   46 use strict qw/subs vars/;
  8         15  
  8         234  
4 8     8   62 use vars qw/$VERSION/;
  8         17  
  8         341  
5 8     8   58 use Scalar::Util qw/reftype weaken/;
  8         19  
  8         545  
6 8     8   56 use Carp;
  8         25  
  8         472  
7 8     8   4464 use SUPER;
  8         22116  
  8         60  
8             $VERSION = '0.175.0';
9              
10             our $STRICT_MODE;
11              
12             sub import {
13 8     8   77 my ( $class, @args ) = @_;
14              
15 8         20 foreach my $arg (@args) {
16 1 50       5 if ( $arg eq 'strict' ) {
17 1         3 $STRICT_MODE = 1;
18             }
19             else {
20 0         0 warn "Test::MockModule unknown import option '$arg'";
21             }
22             }
23              
24 8         3445 return;
25             }
26             my %mocked;
27             sub new {
28 20     20 1 9143 my $class = shift;
29 20         58 my ($package, %args) = @_;
30 20 100 100     144 if ($package && (my $existing = $mocked{$package})) {
31 1         4 return $existing;
32             }
33              
34 19 100 100     269 croak "Cannot mock $package" if $package && $package eq $class;
35 18 100       55 unless (_valid_package($package)) {
36 2 100       15 $package = 'undef' unless defined $package;
37 2         172 croak "Invalid package name $package";
38             }
39              
40 16 100 100     179 unless ($package eq "CORE::GLOBAL" || $package eq 'main' || $args{no_auto} || ${"$package\::VERSION"}) {
  10   100     77  
      100        
41 4         15 (my $load_package = "$package.pm") =~ s{::}{/}g;
42 4         27 TRACE("$package is empty, loading $load_package");
43 4         344 require $load_package;
44             }
45              
46 16         196 TRACE("Creating MockModule object for $package");
47 16         72 my $self = bless {
48             _package => $package,
49             _mocked => {},
50             }, $class;
51 16         53 $mocked{$package} = $self;
52 16         79 weaken $mocked{$package};
53 16         60 return $self;
54             }
55              
56             sub DESTROY {
57 16     16   8216 my $self = shift;
58 16         60 $self->unmock_all;
59             }
60              
61             sub get_package {
62 1     1 1 830 my $self = shift;
63 1         6 return $self->{_package};
64             }
65              
66             sub redefine {
67 9     9 1 62 my ($self, @mocks) = (shift, @_);
68              
69 9         53 while ( my ($name, $value) = splice @mocks, 0, 2 ) {
70 9         37 my $sub_name = $self->_full_name($name);
71 9         19 my $coderef = *{$sub_name}{'CODE'};
  9         39  
72 9 100       56 next if 'CODE' eq ref $coderef;
73              
74 4 50       43 if ( $sub_name =~ qr{^(.+)::([^:]+)$} ) {
75 4         15 my ( $pkg, $sub ) = ( $1, $2 );
76 4 100       48 next if $pkg->can( $sub );
77             }
78              
79 3 50       16 if ('CODE' ne ref $coderef) {
80 3         426 croak "$sub_name does not exist!";
81             }
82             }
83              
84 6         22 return $self->_mock(@_);
85             }
86              
87             sub define {
88 5     5 1 630 my ($self, @mocks) = (shift, @_);
89              
90 5         26 while ( my ($name, $value) = splice @mocks, 0, 2 ) {
91 5         15 my $sub_name = $self->_full_name($name);
92 5         10 my $coderef = *{$sub_name}{'CODE'};
  5         26  
93              
94 5 100       39 if ('CODE' eq ref $coderef) {
95 1         171 croak "$sub_name exists!";
96             }
97             }
98              
99 4         19 return $self->_mock(@_);
100             }
101              
102             sub mock {
103 16     16 1 6381 my ($self, @mocks) = (shift, @_);
104              
105 16 100       243 croak "mock is not allowed in strict mode. Please use define or redefine" if $STRICT_MODE;
106              
107 15         39 return $self->_mock(@mocks);
108             }
109              
110             sub _mock {
111 28     28   57 my $self = shift;
112              
113 28         110 while (my ($name, $value) = splice @_, 0, 2) {
114 30     1   103 my $code = sub { };
115 30 100 100     170 if (ref $value && reftype $value eq 'CODE') {
    100          
116 11         36 $code = $value;
117             } elsif (defined $value) {
118 17     15   76 $code = sub {$value};
  15         714  
119             }
120              
121 30         143 TRACE("$name: $code");
122 30 100       68 croak "Invalid subroutine name: $name" unless _valid_subname($name);
123 29         86 my $sub_name = _full_name($self, $name);
124 29 100       84 if (!$self->{_mocked}{$name}) {
125 24         155 TRACE("Storing existing $sub_name");
126 24         63 $self->{_mocked}{$name} = 1;
127 24 100       36 if (defined &{$sub_name}) {
  24         97  
128 13         45 $self->{_orig}{$name} = \&$sub_name;
129             } else {
130 11         24 $self->{_orig}{$name} = undef;
131             }
132             }
133 29         88 TRACE("Installing mocked $sub_name");
134 29         55 _replace_sub($sub_name, $code);
135             }
136              
137 27         96 return $self;
138             }
139              
140             sub noop {
141 3     3 1 1269 my $self = shift;
142              
143 3 100       84 croak "noop is not allowed in strict mode. Please use define or redefine" if $STRICT_MODE;
144              
145 2         8 $self->_mock($_,1) for @_;
146              
147 2         9 return;
148             }
149              
150             sub original {
151 7     7 1 4346 my $self = shift;
152 7         16 my ($name) = @_;
153             return carp _full_name($self, $name) . " is not mocked"
154 7 100       25 unless $self->{_mocked}{$name};
155 6 100       41 return defined $self->{_orig}{$name} ? $self->{_orig}{$name} : $self->{_package}->super($name);
156             }
157             sub unmock {
158 28     28 1 4501 my $self = shift;
159              
160 28 100       149 carp 'Nothing to unmock' unless @_;
161 28         82 for my $name (@_) {
162 27 100       55 croak "Invalid subroutine name: $name" unless _valid_subname($name);
163              
164 26         69 my $sub_name = _full_name($self, $name);
165 26 100       95 unless ($self->{_mocked}{$name}) {
166 2         147 carp $sub_name . " was not mocked";
167 2         79 next;
168             }
169              
170 24         93 TRACE("Restoring original $sub_name");
171 24         73 _replace_sub($sub_name, $self->{_orig}{$name});
172 24         56 delete $self->{_mocked}{$name};
173 24         55 delete $self->{_orig}{$name};
174             }
175 27         53 return $self;
176             }
177              
178             sub unmock_all {
179 17     17 1 34 my $self = shift;
180 17         27 foreach (keys %{$self->{_mocked}}) {
  17         74  
181 18         48 $self->unmock($_);
182             }
183              
184 17         1044 return;
185             }
186              
187             sub is_mocked {
188 5     5 1 1899 my $self = shift;
189 5         9 my ($name) = shift;
190 5         25 return $self->{_mocked}{$name};
191             }
192              
193             sub _full_name {
194 70     70   152 my ($self, $sub_name) = @_;
195 70         398 sprintf "%s::%s", $self->{_package}, $sub_name;
196             }
197              
198             sub _valid_package {
199 18 100   18   173 defined($_[0]) && $_[0] =~ /^[a-z_]\w*(?:::\w+)*$/i;
200             }
201              
202             sub _valid_subname {
203 57     57   519 $_[0] =~ /^[a-z_]\w*$/i;
204             }
205              
206             sub _replace_sub {
207 53     53   109 my ($sub_name, $coderef) = @_;
208              
209 8     8   13160 no warnings 'redefine';
  8         19  
  8         359  
210 8     8   54 no warnings 'prototype';
  8         22  
  8         2247  
211              
212 53 100       141 if (defined $coderef) {
213 42         61 *{$sub_name} = $coderef;
  42         258  
214             } else {
215 11         38 TRACE("removing subroutine: $sub_name");
216 11         60 my ($package, $sub) = $sub_name =~ /(.*::)(.*)/;
217 11         27 my %symbols = %{$package};
  11         105  
218              
219             # save a copy of all non-code slots
220 11         31 my %slot;
221 11         30 foreach (qw(ARRAY FORMAT HASH IO SCALAR)) {
222 55 100       78 next unless defined(my $elem = *{$symbols{$sub}}{$_});
  55         205  
223 12         36 $slot{$_} = $elem;
224             }
225              
226             # clear the symbol table entry for the subroutine
227 11         40 undef *$sub_name;
228              
229             # restore everything except the code slot
230 11 50       38 return unless keys %slot;
231 11         31 foreach (keys %slot) {
232 12         86 *$sub_name = $slot{$_};
233             }
234             }
235             }
236              
237             # Log::Trace stubs
238       138 1   sub TRACE {}
239       0 1   sub DUMP {}
240              
241             1;
242              
243             =pod
244              
245             =head1 NAME
246              
247             Test::MockModule - Override subroutines in a module for unit testing
248              
249             =head1 SYNOPSIS
250              
251             use Module::Name;
252             use Test::MockModule;
253              
254             {
255             my $module = Test::MockModule->new('Module::Name');
256             $module->mock('subroutine', sub { ... });
257             Module::Name::subroutine(@args); # mocked
258              
259             # Same effect, but this will die() if other_subroutine()
260             # doesn't already exist, which is often desirable.
261             $module->redefine('other_subroutine', sub { ... });
262              
263             # This will die() if another_subroutine() is defined.
264             $module->define('another_subroutine', sub { ... });
265             }
266              
267             {
268             # you can also chain new/mock/redefine/define
269              
270             Test::MockModule->new('Module::Name')
271             ->mock( one_subroutine => sub { ... })
272             ->redefine( other_subroutine => sub { ... } )
273             ->define( a_new_sub => 1234 );
274             }
275              
276             Module::Name::subroutine(@args); # original subroutine
277              
278             # Working with objects
279             use Foo;
280             use Test::MockModule;
281             {
282             my $mock = Test::MockModule->new('Foo');
283             $mock->mock(foo => sub { print "Foo!\n"; });
284              
285             my $foo = Foo->new();
286             $foo->foo(); # prints "Foo!\n"
287             }
288              
289             # If you want to prevent noop and mock from working, you can
290             # load Test::MockModule in strict mode
291              
292             use Test::MockModule qw/strict/;
293             my $module = Test::MockModule->new('Module::Name');
294              
295             # Redefined the other_subroutine or dies if it's not there.
296             $module->redefine('other_subroutine', sub { ... });
297              
298             # Dies since you specified you wanted strict mode.
299             $module->mock('subroutine', sub { ... });
300              
301             =head1 DESCRIPTION
302              
303             C lets you temporarily redefine subroutines in other packages
304             for the purposes of unit testing.
305              
306             A C object is set up to mock subroutines for a given
307             module. The object remembers the original subroutine so it can be easily
308             restored. This happens automatically when all MockModule objects for the given
309             module go out of scope, or when you C the subroutine.
310              
311             =head1 METHODS
312              
313             =over 4
314              
315             =item new($package[, %options])
316              
317             Returns an object that will mock subroutines in the specified C<$package>.
318              
319             If there is no C<$VERSION> defined in C<$package>, the module will be
320             automatically loaded. You can override this behaviour by setting the C
321             option:
322              
323             my $mock = Test::MockModule->new('Module::Name', no_auto => 1);
324              
325             =item get_package()
326              
327             Returns the target package name for the mocked subroutines
328              
329             =item is_mocked($subroutine)
330              
331             Returns a boolean value indicating whether or not the subroutine is currently
332             mocked
333              
334             =item mock($subroutine =E \Ecoderef)
335              
336             Temporarily replaces one or more subroutines in the mocked module. A subroutine
337             can be mocked with a code reference or a scalar. A scalar will be recast as a
338             subroutine that returns the scalar.
339              
340             Returns the current C object, so you can chain L with L.
341              
342             my $mock = Test::MockModule->new->(...)->mock(...);
343              
344             The following statements are equivalent:
345              
346             $module->mock(purge => 'purged');
347             $module->mock(purge => sub { return 'purged'});
348              
349             When dealing with references, things behave slightly differently. The following
350             statements are B equivalent:
351              
352             # Returns the same arrayref each time, with the localtime() at time of mocking
353             $module->mock(updated => [localtime()]);
354             # Returns a new arrayref each time, with up-to-date localtime() value
355             $module->mock(updated => sub { return [localtime()]});
356              
357             The following statements are in fact equivalent:
358              
359             my $array_ref = [localtime()]
360             $module->mock(updated => $array_ref)
361             $module->mock(updated => sub { return $array_ref });
362              
363              
364             However, C is a special case. If you mock a subroutine with C it
365             will install an empty subroutine
366              
367             $module->mock(purge => undef);
368             $module->mock(purge => sub { });
369              
370             rather than a subroutine that returns C:
371              
372             $module->mock(purge => sub { undef });
373              
374             You can call C for the same subroutine many times, but when you call
375             C, the original subroutine is restored (not the last mocked
376             instance).
377              
378             B
379              
380             If you are trying to mock a subroutine exported from another module, this may
381             not behave as you initially would expect, since Test::MockModule is only mocking
382             at the target module, not anything importing that module. If you mock the local
383             package, or use a fully qualified function name, you will get the behavior you
384             desire:
385              
386             use Test::MockModule;
387             use Test::More;
388             use POSIX qw/strftime/;
389              
390             my $posix = Test::MockModule->new("POSIX");
391              
392             $posix->mock("strftime", "Yesterday");
393             is strftime("%D", localtime(time)), "Yesterday", "`strftime` was mocked successfully"; # Fails
394             is POSIX::strftime("%D", localtime(time)), "Yesterday", "`strftime` was mocked successfully"; # Succeeds
395              
396             my $main = Test::MockModule->new("main", no_auto => 1);
397             $main->mock("strftime", "today");
398             is strftime("%D", localtime(time)), "today", "`strftime` was mocked successfully"; # Succeeds
399              
400             If you are trying to mock a subroutine that was exported into a module that you're
401             trying to test, rather than mocking the subroutine in its originating module,
402             you can instead mock it in the module you are testing:
403              
404             package MyModule;
405             use POSIX qw/strftime/;
406              
407             sub minus_twentyfour
408             {
409             return strftime("%a, %b %d, %Y", localtime(time - 86400));
410             }
411              
412             package main;
413             use Test::More;
414             use Test::MockModule;
415              
416             my $posix = Test::MockModule->new("POSIX");
417             $posix->mock("strftime", "Yesterday");
418              
419             is MyModule::minus_twentyfour(), "Yesterday", "`minus-twentyfour` got mocked"; # fails
420              
421             my $mymodule = Test::MockModule->new("MyModule", no_auto => 1);
422             $mymodule->mock("strftime", "Yesterday");
423             is MyModule::minus_twentyfour(), "Yesterday", "`minus-twentyfour` got mocked"; # succeeds
424              
425             =item redefine($subroutine)
426              
427             The same behavior as C, but this will preemptively check to be
428             sure that all passed subroutines actually exist. This is useful to ensure that
429             if a mocked module's interface changes the test doesn't just keep on testing a
430             code path that no longer behaves consistently with the mocked behavior.
431              
432             Note that redefine is also now checking if one of the parent provides the sub
433             and will not die if it's available in the chain.
434              
435             Returns the current C object, so you can chain L with L.
436              
437             my $mock = Test::MockModule->new->(...)->redefine(...);
438              
439             =item define($subroutine)
440              
441             The reverse of redefine, this will fail if the passed subroutine exists.
442             While this use case is rare, there are times where the perl code you are
443             testing is inspecting a package and adding a missing subroutine is actually
444             what you want to do.
445              
446             By using define, you're asserting that the subroutine you want to be mocked
447             should not exist in advance.
448              
449             Note: define does not check for inheritance like redefine.
450              
451             Returns the current C object, so you can chain L with L.
452              
453             my $mock = Test::MockModule->new->(...)->define(...);
454              
455             =item original($subroutine)
456              
457             Returns the original (unmocked) subroutine
458              
459             Here is a sample how to wrap a function with custom arguments using the original subroutine.
460             This is useful when you cannot (do not) want to alter the original code to abstract
461             one hardcoded argument pass to a function.
462              
463             package MyModule;
464              
465             sub sample {
466             return get_path_for("/a/b/c/d");
467             }
468              
469             sub get_path_for {
470             ... # anything goes there...
471             }
472              
473             package main;
474             use Test::MockModule;
475              
476             my $mock = Test::MockModule->new("MyModule");
477             # replace all calls to get_path_for using a different argument
478             $mock->redefine("get_path_for", sub {
479             return $mock->original("get_path_for")->("/my/custom/path");
480             });
481              
482             # or
483              
484             $mock->redefine("get_path_for", sub {
485             my $path = shift;
486             if ( $path && $path eq "/a/b/c/d" ) {
487             # only alter calls with path set to "/a/b/c/d"
488             return $mock->original("get_path_for")->("/my/custom/path");
489             } else { # preserve the original arguments
490             return $mock->original("get_path_for")->($path, @_);
491             }
492             });
493              
494              
495             =item unmock($subroutine [, ...])
496              
497             Restores the original C<$subroutine>. You can specify a list of subroutines to
498             C in one go.
499              
500             =item unmock_all()
501              
502             Restores all the subroutines in the package that were mocked. This is
503             automatically called when all C objects for the given package
504             go out of scope.
505              
506             =item noop($subroutine [, ...])
507              
508             Given a list of subroutine names, mocks each of them with a no-op subroutine. Handy
509             for mocking methods you want to ignore!
510              
511             # Neuter a list of methods in one go
512             $module->noop('purge', 'updated');
513              
514              
515             =back
516              
517             =over 4
518              
519             =item TRACE
520              
521             A stub for Log::Trace
522              
523             =item DUMP
524              
525             A stub for Log::Trace
526              
527             =back
528              
529             =head1 SEE ALSO
530              
531             L
532              
533             L
534              
535             =head1 AUTHORS
536              
537             Current Maintainer: Geoff Franks
538              
539             Original Author: Simon Flack Esimonflk _AT_ cpan.orgE
540              
541             =head1 COPYRIGHT
542              
543             Copyright 2004 Simon Flack Esimonflk _AT_ cpan.orgE.
544             All rights reserved
545              
546             You may distribute under the terms of either the GNU General Public License or
547             the Artistic License, as specified in the Perl README file.
548              
549             =cut