File Coverage

blib/lib/Test/Mocha/Mock.pm
Criterion Covered Total %
statement 82 82 100.0
branch 26 26 100.0
condition 3 3 100.0
subroutine 16 16 100.0
pod 0 5 100.0
total 127 132 100.0


line stmt bran cond sub pod time code
1             package Test::Mocha::Mock;
2             # ABSTRACT: Mock objects
3             $Test::Mocha::Mock::VERSION = '0.61';
4 21     21   73 use strict;
  21         26  
  21         520  
5 21     21   66 use warnings;
  21         22  
  21         462  
6              
7 21     21   77 use Carp 1.22 qw( croak );
  21         353  
  21         811  
8 21     21   6058 use Test::Mocha::MethodCall;
  21         51  
  21         522  
9 21     21   6359 use Test::Mocha::MethodStub;
  21         34  
  21         460  
10 21     21   90 use Test::Mocha::Types qw( Matcher Slurpy );
  21         22  
  21         95  
11 21         946 use Test::Mocha::Util qw( extract_method_name find_caller find_stub
12 21     21   6173 getattr has_caller_package );
  21         24  
13 21     21   70 use Types::Standard qw( ArrayRef HashRef Str );
  21         23  
  21         87  
14 21     21   18999 use UNIVERSAL::ref;
  21         2019471  
  21         145  
15              
16             our $AUTOLOAD;
17             our $num_method_calls = 0;
18             our $last_method_call;
19             our $last_execution;
20              
21             # Classes for which mock isa() should return false
22             my %Isnota = (
23             'Type::Tiny' => undef,
24             'Moose::Meta::TypeConstraint' => undef,
25             );
26              
27             # can() should always return a reference to the C method
28             my $CAN = Test::Mocha::MethodStub->new(
29             name => 'can',
30             args => [Str],
31             executions => [
32             sub {
33             my ( $self, $method_name ) = @_;
34             return sub {
35             $AUTOLOAD = $method_name;
36             goto &AUTOLOAD;
37             };
38             }
39             ],
40             );
41              
42             # DOES() should always return true
43             my $DOES_UC = Test::Mocha::MethodStub->new(
44             name => 'DOES',
45             args => [Str],
46             executions => [ sub { 1 } ],
47             );
48              
49             # does() should always return true
50             my $DOES_LC = Test::Mocha::MethodStub->new(
51             name => 'does',
52             args => [Str],
53             executions => [ sub { 1 } ],
54             );
55              
56             # isa() should always returns true
57             my $ISA = Test::Mocha::MethodStub->new(
58             name => 'isa',
59             args => [Str],
60             executions => [ sub { 1 } ],
61             );
62              
63             sub new {
64             # uncoverable pod
65 61     61 0 117 my ( $class, $mocked_class ) = @_;
66              
67 61         507 my %args = (
68             # ArrayRef[ MethodCall ]
69             calls => [],
70             # $method_name => ArrayRef[ MethodStub ]
71             mocked_class => $mocked_class,
72             stubs => {
73             can => [$CAN],
74             DOES => [$DOES_UC],
75             does => [$DOES_LC],
76             isa => [$ISA],
77             },
78             );
79 61         257 return bless \%args, $class;
80             }
81              
82             sub AUTOLOAD {
83 513     513   32450 my ( $self, @args ) = @_;
84 513         995 my $method_name = extract_method_name($AUTOLOAD);
85              
86             # If a class method or module function, then transform method name
87 513         980 my $mocked_class = getattr( $self, 'mocked_class' );
88 513 100       810 if ($mocked_class) {
89 14 100       24 if ( $args[0] eq $mocked_class ) {
90 7         5 shift @args;
91 7         13 $method_name = "${mocked_class}->${method_name}";
92             }
93             else {
94 7         10 $method_name = "${mocked_class}::${method_name}";
95             }
96             }
97              
98 513         493 undef $last_method_call;
99 513         801 undef $last_execution;
100              
101             # check slurpy type constraint
102             {
103 513         828 my $i = 0;
  513         439  
104 513         411 my $seen_slurpy;
105 513         657 foreach (@args) {
106 458 100       921 if ( Slurpy->check($_) ) {
107 46         1249 $seen_slurpy = 1;
108 46         65 last;
109             }
110 412         5122 $i++;
111             }
112 513 100       1723 croak 'No arguments allowed after a slurpy type constraint'
113             if $i < $#args;
114              
115 505 100       874 if ($seen_slurpy) {
116 38         58 my $slurpy = $args[$i]->{slurpy};
117 38 100 100     82 croak 'Slurpy argument must be a type of ArrayRef or HashRef'
118             unless $slurpy->is_a_type_of(ArrayRef)
119             || $slurpy->is_a_type_of(HashRef);
120             }
121             }
122              
123 499         10343 $num_method_calls++;
124              
125 499         816 my $calls = getattr( $self, 'calls' );
126 499         794 my $stubs = getattr( $self, 'stubs' );
127              
128             # record the method call for verification
129 499         1028 $last_method_call = Test::Mocha::MethodCall->new(
130             invocant => $self,
131             name => $method_name,
132             args => \@args,
133             caller => [find_caller],
134             );
135 499         613 push @{$calls}, $last_method_call;
  499         685  
136              
137             # find a stub to return a response
138 499         868 my $stub = find_stub( $self, $last_method_call );
139 499 100       893 if ( defined $stub ) {
140             # save reference to stub execution so it can be restored
141 135         212 my $executions = getattr( $stub, 'executions' );
142 135 100       101 $last_execution = $executions->[0] if @{$executions} > 1;
  135         253  
143              
144 135         319 return $stub->do_next_execution( $self, @args );
145             }
146 364         826 return;
147             }
148              
149             # Let AUTOLOAD() handle the UNIVERSAL methods
150              
151             sub isa {
152             # uncoverable pod
153 107     107 0 2048 my ( $self, $class ) = @_;
154              
155             # Handle internal calls from UNIVERSAL::ref::_hook()
156             # when ref($mock) is called
157 107 100       424 return 1 if $class eq __PACKAGE__;
158              
159             # In order to allow mock methods to be called with other mocks as
160             # arguments, mocks cannot have isa() called with type constraints,
161             # which are not allowed as arguments.
162 57 100       226 return if exists $Isnota{$class};
163              
164 9         13 $AUTOLOAD = 'isa';
165 9         24 goto &AUTOLOAD;
166             }
167              
168             sub DOES {
169             # uncoverable pod
170 42     42 0 218 my ( $self, $role ) = @_;
171              
172             # Handle internal calls from UNIVERSAL::ref::_hook()
173             # when ref($mock) is called
174 42 100       80 return 1 if $role eq __PACKAGE__;
175              
176 21 100       70 return if !ref $self;
177              
178 9         42 $AUTOLOAD = 'DOES';
179 9         21 goto &AUTOLOAD;
180             }
181              
182             sub can {
183             # uncoverable pod
184 48     48 0 5725 my ( $self, $method_name ) = @_;
185              
186             # Handle can('CARP_TRACE') for internal croak()'s (Carp v1.32+)
187 48 100       663 return if $method_name eq 'CARP_TRACE';
188              
189 9         10 $AUTOLOAD = 'can';
190 9         23 goto &AUTOLOAD;
191             }
192              
193             sub ref { ## no critic (ProhibitBuiltinHomonyms)
194             # uncoverable pod
195 12     12 0 41 $AUTOLOAD = 'ref';
196 12         26 goto &AUTOLOAD;
197             }
198              
199             # Don't let AUTOLOAD() handle DESTROY() so that object can be destroyed
200 1     1   2 sub DESTROY { }
201              
202             1;