File Coverage

blib/lib/Test/MonkeyMock.pm
Criterion Covered Total %
statement 136 138 98.5
branch 51 62 82.2
condition 26 30 86.6
subroutine 18 19 94.7
pod 8 8 100.0
total 239 257 93.0


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