File Coverage

blib/lib/Test/MonkeyMock.pm
Criterion Covered Total %
statement 137 139 98.5
branch 53 64 82.8
condition 26 30 86.6
subroutine 18 18 100.0
pod 8 8 100.0
total 242 259 93.4


line stmt bran cond sub pod time code
1             package Test::MonkeyMock;
2              
3 2     2   136823 use strict;
  2         14  
  2         59  
4 2     2   11 use warnings;
  2         4  
  2         250  
5              
6             require Carp;
7              
8             our $VERSION = '0.11';
9              
10             my $registry = {};
11             my $magic_counter = 0;
12              
13             sub new {
14 33     33 1 72100 my $class = shift;
15 33 50       95 $class = ref $class if ref $class;
16 33         59 my ($instance) = @_;
17              
18 33         43 my $new_package;
19              
20 33 100       76 if ($instance) {
21 19         51 $new_package =
22             __PACKAGE__ . '::'
23             . ref($instance)
24             . '::__instance__'
25             . ($magic_counter++);
26              
27 2     2   13 no strict 'refs';
  2         4  
  2         148  
28 19         31 @{$new_package . '::ISA'} = (ref($instance));
  19         409  
29             }
30             else {
31 14         26 $instance = {};
32 14         30 $new_package = __PACKAGE__ . '::' . ($magic_counter++);
33              
34 2     2   12 no strict 'refs';
  2         5  
  2         77  
35 14         18 @{$new_package . '::ISA'} = __PACKAGE__;
  14         286  
36             }
37              
38 2     2   10 no strict 'refs';
  2         10  
  2         646  
39 33         118 for my $method (
40             qw/
41             mock
42             mocked_called
43             mocked_call_args
44             mocked_call_stack
45             mocked_return_args
46             mocked_return_stack
47             /
48             )
49             {
50 198     74   575 *{$new_package . '::' . $method} = sub { goto &$method };
  198         852  
  74         2156  
51             }
52              
53 33         101 bless $instance, $new_package;
54              
55 33         111 return $instance;
56             }
57              
58             sub mock {
59 34     34 1 59 my $self = shift;
60 34         70 my ($method, $code, %options) = @_;
61              
62 34 100       125 if (ref($self) =~ m/__instance__/) {
63 17 100       291 Carp::croak("Unknown method '$method'")
64             unless my $orig_method = $self->can($method);
65              
66 16 100       65 if (exists $registry->{ref($self)}->{'mocks'}->{$method}) {
67 1         2 push @{$registry->{ref($self)}->{'mocks'}->{$method}},
  1         4  
68             {code => $code, orig_code => $orig_method};
69 1         4 return $self;
70             }
71              
72 15         27 my $ref_self = ref($self);
73 15         22 my $package = __PACKAGE__;
74 15         164 $ref_self =~ s/^${package}::(.*)::__instance__\d+/$1/;
75              
76 15         71 my $new_package =
77             __PACKAGE__ . '::' . $ref_self . '::__instance__' . $magic_counter++;
78              
79 15         42 $registry->{$new_package} = $registry->{ref($self)};
80              
81 15   50     44 my $mocks = $registry->{$new_package}->{'mocks'} ||= {};
82 15         55 $mocks->{$method} =
83             [{code => $code, orig_code => $orig_method, options => \%options}];
84              
85 2     2   14 no strict 'refs';
  2         4  
  2         2790  
86 15         29 @{$new_package . '::ISA'} = ref($self);
  15         305  
87 15         79 *{$new_package . '::' . $method} = sub {
88 26     26   125 _dispatch($new_package, $method, @_);
89 15         85 };
90              
91 15         69 bless $self, $new_package;
92             }
93             else {
94 17   100     83 my $mocks = $registry->{ref($self)}->{'mocks'} ||= {};
95 17         28 push @{$mocks->{$method}},
  17         57  
96             {
97             code => $code,
98             options => \%options
99             };
100             }
101              
102 32         82 return $self;
103             }
104              
105             sub mocked_called {
106 6     6 1 13 my $self = shift;
107 6         13 my ($method) = @_;
108              
109 6   100     27 my $mocks = $registry->{ref($self)}->{'mocks'} ||= {};
110 6   100     22 my $calls = $registry->{ref($self)}->{'calls'} ||= {};
111              
112 6 100       26 if (ref($self) =~ m/__instance__/) {
113 3 100       96 Carp::croak("Unknown method '$method'")
114             unless $self->can($method);
115             }
116             else {
117             Carp::croak("Unmocked method '$method'")
118 3 100       74 unless exists $mocks->{$method};
119             }
120              
121 4   100     22 return $calls->{$method}->{called} || 0;
122             }
123              
124             sub mocked_call_args {
125 10     10 1 21 my $self = shift;
126 10         22 my ($method, $frame) = @_;
127              
128 10   100     34 $frame ||= 0;
129              
130 10         25 my $stack = $self->mocked_call_stack($method);
131              
132 8 100       254 Carp::croak("Unknown frame '$frame'")
133             unless @$stack > $frame;
134              
135 6         10 return @{$stack->[$frame]};
  6         41  
136             }
137              
138             sub mocked_call_stack {
139 11     11 1 18 my $self = shift;
140 11         19 my ($method) = @_;
141              
142 11 50       24 Carp::croak("Method is required") unless $method;
143              
144 11   100     39 my $calls = $registry->{ref($self)}->{'calls'} ||= {};
145 11   100     40 my $mocks = $registry->{ref($self)}->{'mocks'} ||= {};
146              
147 11 100       41 if (ref($self) =~ m/__instance__/) {
148 6 100       112 Carp::croak("Unknown method '$method'")
149             unless $self->can($method);
150             }
151             else {
152             Carp::croak("Unmocked method '$method'")
153 5 100       83 unless exists $mocks->{$method};
154             }
155              
156             Carp::croak("Method '$method' was not called")
157 9 50       22 unless exists $calls->{$method};
158              
159 9         23 return $calls->{$method}->{stack};
160             }
161              
162             sub mocked_return_args {
163 6     6 1 13 my $self = shift;
164 6         14 my ($method, $frame) = @_;
165              
166 6   100     23 $frame ||= 0;
167              
168 6         23 my $stack = $self->mocked_return_stack($method);
169              
170 6 50       15 Carp::croak("Unknown frame '$frame'")
171             unless @$stack > $frame;
172              
173 6         10 return @{$stack->[$frame]};
  6         30  
174             }
175              
176             sub mocked_return_stack {
177 7     7 1 12 my $self = shift;
178 7         14 my ($method) = @_;
179              
180 7 50       16 Carp::croak("Method is required") unless $method;
181              
182 7   50     35 my $returns = $registry->{ref($self)}->{'returns'} ||= {};
183 7   50     19 my $mocks = $registry->{ref($self)}->{'mocks'} ||= {};
184              
185 7 100       31 if (ref($self) =~ m/__instance__/) {
186 4 50       20 Carp::croak("Unknown method '$method'")
187             unless $self->can($method);
188             }
189             else {
190             Carp::croak("Unmocked method '$method'")
191 3 50       8 unless exists $mocks->{$method};
192             }
193              
194             Carp::croak("Method '$method' was not called")
195 7 50       18 unless exists $returns->{$method};
196              
197 7         32 return $returns->{$method}->{stack};
198             }
199              
200             sub can {
201 5     5 1 20 my $self = shift;
202 5         10 my ($method) = @_;
203              
204 5 50       15 if (ref($self) =~ m/__instance__/) {
205 0         0 return $self->can($method);
206             }
207             else {
208 5   50     17 my $mocks = $registry->{ref($self)}->{'mocks'} ||= {};
209 5         74 return $mocks->{$method}->[0]->{code};
210             }
211             }
212              
213             our $AUTOLOAD;
214              
215             sub AUTOLOAD {
216 33     33   8804 my $self = shift;
217              
218 33         121 my ($method) = (split /::/, $AUTOLOAD)[-1];
219              
220 33 100       169 return if $method =~ /^[A-Z]+$/;
221              
222 19         52 return _dispatch(ref($self), $method, $self, @_);
223             }
224              
225             sub _dispatch {
226 45     45   70 my $ref_self = shift;
227 45         71 my $method = shift;
228              
229 45   100     144 my $calls = $registry->{$ref_self}->{'calls'} ||= {};
230 45   100     135 my $returns = $registry->{$ref_self}->{'returns'} ||= {};
231 45   100     94 my $mocks = $registry->{$ref_self}->{'mocks'} ||= {};
232              
233             Carp::croak("Unmocked method '$method'")
234 45 100       159 if !exists $mocks->{$method};
235              
236 44         63 foreach my $mock (@{$mocks->{$method}}) {
  44         96  
237 51 100       118 if (my $options = $mock->{options}) {
238 50 100       94 if (my $when = $options->{when}) {
239 4 100       11 next unless $when->(@_);
240             }
241              
242 48 100       102 if (defined(my $frame = $options->{frame})) {
243             my $current_frame =
244             $returns->{$method}->{stack}
245 7 100       14 ? @{$returns->{$method}->{stack}}
  6         10  
246             : 0;
247 7 100       18 next unless $frame == $current_frame;
248             }
249             }
250              
251 44         84 $calls->{$method}->{called}++;
252              
253 44         88 push @{$calls->{$method}->{stack}}, [@_[1 .. $#_]];
  44         142  
254              
255 44         77 my @result;
256              
257 44 100       88 if (my $code = $mock->{code}) {
    50          
258 41         88 @result = $code->(@_);
259             }
260             elsif (my $orig_code = $mock->{orig_code}) {
261 3         7 @result = $orig_code->(@_);
262             }
263             else {
264 0         0 Carp::croak("Unmocked method '$method'");
265             }
266              
267 44         158 push @{$returns->{$method}->{stack}}, [@result];
  44         104  
268              
269 44 50       210 return wantarray ? @result : $result[0];
270             }
271             }
272              
273             1;
274             __END__