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.66';
4 13     13   91 use parent 'Test::Mocha::SpyBase';
  13         30  
  13         72  
5 13     13   904 use strict;
  13         40  
  13         313  
6 13     13   70 use warnings;
  13         24  
  13         412  
7              
8 13     13   64 use Carp 1.22 ();
  13         284  
  13         266  
9 13     13   110 use Scalar::Util ();
  13         43  
  13         322  
10 13     13   81 use Test::Mocha::MethodCall;
  13         22  
  13         314  
11 13     13   69 use Test::Mocha::MethodStub;
  13         24  
  13         325  
12 13     13   90 use Test::Mocha::Util ();
  13         26  
  13         235  
13 13     13   61 use Types::Standard ();
  13         38  
  13         387  
14 13     13   77 use if $] lt '5.025', 'UNIVERSAL::ref';
  13         24  
  13         92  
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       233 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         34 map { $_ => [ $DEFAULT_STUBS{$_} ] }
  14         56  
60             keys %DEFAULT_STUBS
61             };
62 7         31 return bless $args, $class;
63             }
64              
65             sub __object {
66 135     135   1175 my ($self) = @_;
67 135         1049 return $self->{object};
68             }
69              
70             sub AUTOLOAD {
71 142     142   22975 my ( $self, @args ) = @_;
72 142         489 Test::Mocha::Util::check_slurpy_arg(@args);
73              
74 135         295 my $method_name = Test::Mocha::Util::extract_method_name($AUTOLOAD);
75              
76             # record the method call for verification
77 135         367 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       434 if ( $self->__CaptureMode ) {
85 61 100 100     160 if (
86             !$self->__object->can($method_name)
87             # allow ref() to be recorded and verified
88             && $method_name ne 'ref'
89             )
90             {
91 3         9 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         195 $self->__CaptureMethodCall($method_call);
99 58         153 return;
100             }
101              
102             # record the method call to allow for verification
103 74         123 push @{ $self->__calls }, $method_call;
  74         183  
104              
105             # find a stub to return a response
106 74 100       195 if ( my $stub = $self->__find_stub($method_call) ) {
107 41         5965 return $stub->execute_next_response( $self, @args );
108             }
109              
110             # delegate the method call to the real object
111             Carp::croak(
112 33 100       77 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         76 return $self->__object->$method_name(@args);
119             }
120              
121             sub isa {
122             # uncoverable pod
123 7     7 0 692 my ( $self, $class ) = @_;
124              
125             # Don't let AUTOLOAD handle internal isa() calls
126 7 100       58 return 1 if $self->SUPER::isa($class);
127              
128 5         10 $AUTOLOAD = 'isa';
129 5         24 goto &AUTOLOAD;
130             }
131              
132             sub DOES {
133             # uncoverable pod
134 5     5 0 22 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         9 $AUTOLOAD = 'DOES';
143 5         17 goto &AUTOLOAD;
144             }
145              
146             sub can {
147             # uncoverable pod
148 22     22 0 7527 my ( $self, $method_name ) = @_;
149              
150             # Handle can('CARP_TRACE') for internal croak()'s (Carp v1.32+)
151 22 100       301 return if $method_name eq 'CARP_TRACE';
152              
153 5         9 $AUTOLOAD = 'can';
154 5         21 goto &AUTOLOAD;
155             }
156              
157             # Don't let AUTOLOAD() handle DESTROY() so that object can be destroyed
158       1     sub DESTROY { }
159              
160             1;