File Coverage

blib/lib/Test/MockModule.pm
Criterion Covered Total %
statement 157 158 99.3
branch 58 66 87.8
condition 25 30 83.3
subroutine 28 29 96.5
pod 12 12 100.0
total 280 295 94.9


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