File Coverage

blib/lib/Test/Mocha/Spy.pm
Criterion Covered Total %
statement 67 67 100.0
branch 16 18 88.8
condition 3 3 100.0
subroutine 17 17 100.0
pod 0 3 100.0
total 103 108 98.1


line stmt bran cond sub pod time code
1             package Test::Mocha::Spy;
2             # ABSTRACT: Spy objects
3             $Test::Mocha::Spy::VERSION = '0.67';
4 13     13   98 use parent 'Test::Mocha::SpyBase';
  13         24  
  13         77  
5 13     13   842 use strict;
  13         32  
  13         252  
6 13     13   72 use warnings;
  13         34  
  13         433  
7              
8 13     13   87 use Carp 1.22 ();
  13         295  
  13         281  
9 13     13   102 use Scalar::Util ();
  13         25  
  13         313  
10 13     13   80 use Test::Mocha::MethodCall;
  13         28  
  13         359  
11 13     13   86 use Test::Mocha::MethodStub;
  13         26  
  13         293  
12 13     13   96 use Test::Mocha::Util ();
  13         23  
  13         248  
13 13     13   69 use Types::Standard ();
  13         28  
  13         379  
14 13     13   85 use if $] lt '5.025', 'UNIVERSAL::ref';
  13         38  
  13         84  
15              
16             our $AUTOLOAD;
17              
18             my %DEFAULT_STUBS = (
19             can => Test::Mocha::MethodStub->new(
20             # can() should return a reference to AUTOLOAD() for all methods
21             name => 'can',
22             args => [Types::Standard::Str],
23             responses => [
24             sub {
25             my ( $self, $method_name ) = @_;
26             return if !$self->__object->can($method_name);
27             return sub {
28             $AUTOLOAD = $method_name;
29             goto &AUTOLOAD;
30             };
31             }
32             ],
33             ),
34             ref => Test::Mocha::MethodStub->new(
35             # ref() is a special stub because we use UNIVERSAL::ref which
36             # allows us to call it as a method even though it's not a method
37             # in the wrapped object.
38             name => 'ref',
39             args => [],
40             responses => [
41             sub {
42             my ($self) = @_;
43             return ref( $self->__object );
44             }
45             ],
46             ),
47             );
48              
49             sub __new {
50             # uncoverable pod
51 8     8   32 my ( $class, $object ) = @_;
52 8 100       226 Carp::croak "Can't spy on an unblessed reference"
53             if !Scalar::Util::blessed($object);
54              
55 7         56 my $args = $class->SUPER::__new;
56              
57 7         28 $args->{object} = $object;
58             $args->{stubs} = {
59 7         46 map { $_ => [ $DEFAULT_STUBS{$_} ] }
  14         56  
60             keys %DEFAULT_STUBS
61             };
62 7         33 return bless $args, $class;
63             }
64              
65             sub __object {
66 135     135   743 my ($self) = @_;
67 135         1151 return $self->{object};
68             }
69              
70             sub AUTOLOAD {
71 142     142   24469 my ( $self, @args ) = @_;
72 142         503 Test::Mocha::Util::check_slurpy_arg(@args);
73              
74 135         312 my $method_name = Test::Mocha::Util::extract_method_name($AUTOLOAD);
75              
76             # record the method call for verification
77 135         430 my $method_call = Test::Mocha::MethodCall->new(
78             invocant => $self,
79             name => $method_name,
80             args => \@args,
81             caller => [Test::Mocha::Util::find_caller],
82             );
83              
84 135 100       437 if ( $self->__CaptureMode ) {
85 61 100 100     154 if (
86             !$self->__object->can($method_name)
87             # allow ref() to be recorded and verified
88             && $method_name ne 'ref'
89             )
90             {
91 3         19 Carp::croak(
92             sprintf
93             qq{Can't %s object method "%s" because it can't be located via package "%s"},
94             $self->__CaptureMode, $method_name, ref( $self->__object )
95             );
96             }
97              
98 58         183 $self->__CaptureMethodCall($method_call);
99 58         133 return;
100             }
101              
102             # record the method call to allow for verification
103 74         120 push @{ $self->__calls }, $method_call;
  74         194  
104              
105             # find a stub to return a response
106 74 100       203 if ( my $stub = $self->__find_stub($method_call) ) {
107 41         6193 return $stub->execute_next_response( $self, @args );
108             }
109              
110             # delegate the method call to the real object
111             Carp::croak(
112 33 100       80 sprintf
113             qq{Can't call object method "%s" because it can't be located via package "%s"},
114             $method_name,
115             ref( $self->__object )
116             ) if !$self->__object->can($method_name);
117              
118 32         71 return $self->__object->$method_name(@args);
119             }
120              
121             sub isa {
122             # uncoverable pod
123 7     7 0 684 my ( $self, $class ) = @_;
124              
125             # Don't let AUTOLOAD handle internal isa() calls
126 7 100       61 return 1 if $self->SUPER::isa($class);
127              
128 5         16 $AUTOLOAD = 'isa';
129 5         21 goto &AUTOLOAD;
130             }
131              
132             sub DOES {
133             # uncoverable pod
134 5     5 0 18 my ( $self, $role ) = @_;
135              
136             # Handle internal calls from UNIVERSAL::ref::_hook()
137             # when ref($mock) is called
138 5 50       48 return 1 if $role eq __PACKAGE__;
139              
140 5 50       15 return if !ref $self;
141              
142 5         10 $AUTOLOAD = 'DOES';
143 5         25 goto &AUTOLOAD;
144             }
145              
146             sub can {
147             # uncoverable pod
148 22     22 0 7814 my ( $self, $method_name ) = @_;
149              
150             # Handle can('CARP_TRACE') for internal croak()'s (Carp v1.32+)
151 22 100       325 return if $method_name eq 'CARP_TRACE';
152              
153 5         10 $AUTOLOAD = 'can';
154 5         17 goto &AUTOLOAD;
155             }
156              
157             # Don't let AUTOLOAD() handle DESTROY() so that object can be destroyed
158       1     sub DESTROY { }
159              
160             1;