File Coverage

blib/lib/Test/TinyMocker.pm
Criterion Covered Total %
statement 77 77 100.0
branch 16 16 100.0
condition 6 7 85.7
subroutine 21 21 100.0
pod 5 5 100.0
total 125 126 99.2


line stmt bran cond sub pod time code
1             package Test::TinyMocker;
2              
3 8     8   546936 use strict;
  8         25  
  8         446  
4 8     8   48 use warnings;
  8         19  
  8         287  
5              
6 8     8   48 use Carp qw{ croak };
  8         21  
  8         995  
7              
8 8     8   48 use vars qw(@EXPORT $VERSION);
  8         18  
  8         987  
9 8     8   76 use base 'Exporter';
  8         13  
  8         5781  
10              
11             $VERSION = '0.03';
12             my $mocks = {};
13              
14             @EXPORT = qw(mock unmock should method methods);
15              
16 10     10 1 5641 sub method($) {@_}
17 4     4 1 1692 sub methods($) {@_}
18 14     14 1 65 sub should(&) {@_}
19              
20             sub mock {
21 34 100   34 1 15702 croak 'useless use of mock with one or less parameter'
22             if scalar @_ < 2;
23              
24             # if the last element is a HashRef, it's options to process
25 27         51 my $options = {};
26 27         49 my $last_elem = $_[-1];
27 27 100       165 if ( ref($last_elem) eq ref( {} ) ) {
28 6         10 $options = pop @_;
29             }
30              
31             # the last element is now the subroutine to use for the mock
32 27         51 my $sub = pop;
33              
34 27         73 my @symbols = _flat_symbols(@_);
35 27   100     155 my $ignore_unknown = $options->{ignore_unknown} || 0;
36              
37 27         2144 foreach my $symbol (@symbols) {
38 34 100 66     129 croak "unknown symbol: $symbol"
39             if !$ignore_unknown && !_symbol_exists($symbol);
40              
41 30         847 _save_sub($symbol);
42 30         58 _bind_coderef_to_symbol( $symbol, $sub );
43             }
44             }
45              
46             sub unmock {
47 8 100   8 1 216 croak 'useless use of unmock without parameters'
48             unless scalar @_;
49              
50 7         19 my @symbols = _flat_symbols(@_);
51 7         16 foreach my $symbol (@symbols) {
52 10 100       172 croak "unkown method $symbol"
53             unless $mocks->{$symbol};
54              
55             {
56 8     8   62 no strict 'refs';
  8         19  
  8         321  
  9         9  
57 8     8   58 no warnings 'redefine', 'prototype';
  8         15  
  8         3262  
58 9         16 *{$symbol} = delete $mocks->{$symbol};
  9         49  
59             }
60             }
61             }
62              
63             sub _flat_symbols {
64 34 100   34   86 if ( @_ == 2 ) {
65 18         61 return ref $_[1] eq 'ARRAY'
66 25 100       1213 ? map {qq{$_[0]::$_}} @{ $_[1] }
  9         18  
67             : qq{$_[0]::$_[1]};
68             }
69             else {
70 2         7 return ref $_[0] eq 'ARRAY'
71 9 100       44 ? @{ $_[0] }
72             : $_[0];
73             }
74             }
75              
76             sub _symbol_exists {
77 28     28   46 my ($symbol) = @_;
78             {
79 8     8   61 no strict 'refs';
  8         18  
  8         630  
  28         35  
80 8     8   46 no warnings 'redefine', 'prototype';
  8         12  
  8         916  
81              
82 28         35 return defined *{$symbol}{CODE};
  28         1375  
83             }
84             }
85              
86             sub _bind_coderef_to_symbol {
87 30     30   61 my ( $symbol, $sub ) = @_;
88             {
89 8     8   154 no strict 'refs';
  8         17  
  8         254  
  30         35  
90 8     8   57 no warnings 'redefine', 'prototype';
  8         27  
  8         1758  
91              
92 30         75 *{$symbol} = $sub;
  30         254  
93             }
94             }
95              
96             sub _save_sub {
97 30     30   43 my ($name) = @_;
98              
99             {
100 8     8   43 no strict 'refs';
  8         13  
  8         1288  
  30         68  
101 30   100     112 $mocks->{$name} ||= *{$name}{CODE};
  18         111  
102             }
103              
104 30         49 return $name;
105             }
106              
107             1;
108              
109              
110             =pod
111              
112             =head1 NAME
113              
114             Test::TinyMocker
115              
116             =head1 VERSION
117              
118             version 0.05
119              
120             =head1 SYNOPSIS
121              
122             use Test::More;
123             use Test::TinyMocker;
124              
125             mock 'Some::Module'
126             => method 'some_method'
127             => should {
128             return $mocked_value;
129             };
130              
131             # or
132              
133             mock 'Some::Module'
134             => methods [ 'this_method', 'that_method' ]
135             => should {
136             return $mocked_value;
137             };
138              
139             # or
140              
141             mock 'Some::Module::some_method'
142             => should {
143             return $mocked_value;
144             };
145              
146             # Some::Module::some_method() will now always return $mocked_value;
147              
148             # To restore the original method
149            
150             unmock 'Some::Module::some_method';
151              
152             # or
153            
154             unmock 'Some::Module' => method 'some_method';
155              
156             # or
157              
158             unmock 'Some::Module' => methods [ 'this_method', 'that_method' ];
159              
160             =head1 NAME
161              
162             Test::TinyMocker - a very simple tool to mock external modules
163              
164             =head1 EXPORT
165              
166             =head2 mock($module, $method_or_methods, $sub, $options)
167              
168             This function allows you to overwrite the given method with an arbitrary code
169             block. This lets you simulate soem kind of behaviour for your tests.
170              
171             Alternatively, this method can be passed only two arguments, the first one will
172             be the full path of the method (pcakge name + method name) and the second one
173             the coderef.
174              
175             An options HashRef can be passed as the last argument. Currently one option is
176             supported: C (default false) which when sets to true allows to
177             mock an unknown symbol.
178              
179             Syntactic sugar is provided (C, C and C) in order to
180             let you write sweet mock statements:
181              
182             # This:
183             mock('Foo::Bar', 'a_method', sub { return 42;});
184              
185             # is the same as:
186             mock 'Foo::Bar' => method 'a_method' => should { return 42 };
187              
188             # or:
189             mock 'Foo::Bar::a_method' => should { return 42 };
190              
191             # or also:
192             mock('Foo::Bar::a_method', sub { return 42;});
193              
194             Using multiple methods at the same time can be done with arrayrefs:
195              
196             # This:
197             mock('Foo::Bar', ['a_method', 'b_method'], sub { 42 } );
198              
199             # is the same as:
200             mock 'Foo::Bar' => methods ['a_method', 'b_method'] => should { 42 };
201              
202             =head2 unmock($module, $method_or_methods)
203              
204             Syntactic sugar is provided (C and C) in order to let you write
205             sweet unmock statements:
206              
207             # This:
208             unmock('Foo::Bar', 'a_method');
209              
210             # is the same as:
211             unmock 'Foo::Bar' => method 'a_method';
212              
213             And using multiple methods at the same time:
214              
215             unmock 'Foo::Bar' => methods ['a_method', 'b_method'];
216              
217             =head2 method
218              
219             Syntactic sugar for mock()
220              
221             =head2 methods
222              
223             Syntactic sugar for mock()
224              
225             =head2 should
226              
227             Syntactic sugar for mock()
228              
229             =head1 AUTHOR
230              
231             Alexis Sukrieh, C<< >>
232              
233             =head1 BUGS
234              
235             Please report any bugs or feature requests to C, or through
236             the web interface at L. I will be notified, and then you'll
237             automatically be notified of progress on your bug as I make changes.
238              
239             =head1 SUPPORT
240              
241             You can find documentation for this module with the perldoc command.
242              
243             perldoc Test::TinyMocker
244              
245             You can also look for information at:
246              
247             =over 4
248              
249             =item * RT: CPAN's request tracker
250              
251             L
252              
253             =item * AnnoCPAN: Annotated CPAN documentation
254              
255             L
256              
257             =item * CPAN Ratings
258              
259             L
260              
261             =item * Search CPAN
262              
263             L
264              
265             =back
266              
267             =head1 ACKNOWLEDGEMENTS
268              
269             This module was inspired by Gugod's blog, after the article published about
270             mocking in Ruby and Perl: L
271              
272             This module was first part of the test tools provided by Dancer in its own t
273             directory (previously named C). A couple of developers asked
274             me if I could released this module as a real Test:: distribution on CPAN, so
275             here it is.
276              
277             =head1 LICENSE AND COPYRIGHT
278              
279             Copyright 2010 Alexis Sukrieh.
280              
281             This program is free software; you can redistribute it and/or modify it
282             under the terms of either: the GNU General Public License as published
283             by the Free Software Foundation; or the Artistic License.
284              
285             See http://dev.perl.org/licenses/ for more information.
286              
287             =head1 AUTHOR
288              
289             Alexis Sukrieh
290              
291             =head1 COPYRIGHT AND LICENSE
292              
293             This software is copyright (c) 2013 by Alexis Sukrieh.
294              
295             This is free software; you can redistribute it and/or modify it under
296             the same terms as the Perl 5 programming language system itself.
297              
298             =cut
299              
300              
301             __END__