line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
=pod |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Test::Mockify - minimal mocking framework for perl |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 SYNOPSIS |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use Test::Mockify; |
10
|
|
|
|
|
|
|
use Test::Mockify::Verify qw ( WasCalled ); |
11
|
|
|
|
|
|
|
use Test::Mockify::Matcher qw ( String ); |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
# build a new mocked object |
14
|
|
|
|
|
|
|
my $MockObjectBuilder = Test::Mockify->new('SampleLogger', []); |
15
|
|
|
|
|
|
|
$MockObjectBuilder->mock('log')->when(String())->thenReturnUndef(); |
16
|
|
|
|
|
|
|
my $MockedLogger = $MockLoggerBuilder->getMockObject(); |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
# inject mocked object into the code you want to test |
19
|
|
|
|
|
|
|
my $App = SampleApp->new('logger'=> $MockedLogger); |
20
|
|
|
|
|
|
|
$App->do_something(); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# verify that the mocked method was called |
23
|
|
|
|
|
|
|
ok(WasCalled($MockedLogger, 'log'), 'log was called'); |
24
|
|
|
|
|
|
|
done_testing(); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 DESCRIPTION |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
Use L to create and configure mock objects. Use L to |
29
|
|
|
|
|
|
|
verify the interactions with your mocks. Use L to inject dependencies into your Sut. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
You can find a Example Project in L |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
It is possible to use alternative constructor name |
34
|
|
|
|
|
|
|
my $MockObjectBuilder = Test::Mockify->new('SampleLogger', [], 'create'); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
=head1 METHODS |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=cut |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
package Test::Mockify; |
41
|
17
|
|
|
17
|
|
453758
|
use Test::Mockify::Tools qw ( Error ExistsMethod LoadPackage ); |
|
17
|
|
|
|
|
60
|
|
|
17
|
|
|
|
|
1102
|
|
42
|
17
|
|
|
17
|
|
9163
|
use Test::Mockify::TypeTests qw ( IsString IsArrayReference); |
|
17
|
|
|
|
|
48
|
|
|
17
|
|
|
|
|
1005
|
|
43
|
17
|
|
|
17
|
|
7035
|
use Test::Mockify::MethodCallCounter; |
|
17
|
|
|
|
|
45
|
|
|
17
|
|
|
|
|
470
|
|
44
|
17
|
|
|
17
|
|
6724
|
use Test::Mockify::Method; |
|
17
|
|
|
|
|
66
|
|
|
17
|
|
|
|
|
565
|
|
45
|
17
|
|
|
17
|
|
7058
|
use Test::Mockify::MethodSpy; |
|
17
|
|
|
|
|
53
|
|
|
17
|
|
|
|
|
519
|
|
46
|
17
|
|
|
17
|
|
8191
|
use Test::MockObject::Extends; |
|
17
|
|
|
|
|
121671
|
|
|
17
|
|
|
|
|
89
|
|
47
|
17
|
|
|
17
|
|
618
|
use Scalar::Util qw( blessed ); |
|
17
|
|
|
|
|
38
|
|
|
17
|
|
|
|
|
771
|
|
48
|
17
|
|
|
17
|
|
7744
|
use Sub::Override; |
|
17
|
|
|
|
|
17192
|
|
|
17
|
|
|
|
|
550
|
|
49
|
|
|
|
|
|
|
|
50
|
17
|
|
|
17
|
|
110
|
use strict; |
|
17
|
|
|
|
|
53
|
|
|
17
|
|
|
|
|
35641
|
|
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
our $VERSION = '2.4'; |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
sub new { |
55
|
114
|
|
|
114
|
0
|
38858
|
my $class = shift; |
56
|
114
|
|
|
|
|
258
|
my ( $FakeModulePath, $aFakeParams, $AlternativeConstructorName ) = @_; |
57
|
|
|
|
|
|
|
|
58
|
114
|
|
|
|
|
238
|
my $self = bless {}, $class; |
59
|
|
|
|
|
|
|
|
60
|
114
|
|
100
|
|
|
550
|
$AlternativeConstructorName //= 'new'; |
61
|
114
|
|
|
|
|
392
|
LoadPackage( $FakeModulePath ); |
62
|
114
|
100
|
|
|
|
739
|
if(!$FakeModulePath->can($AlternativeConstructorName)){ |
63
|
27
|
100
|
|
|
|
70
|
if(defined $aFakeParams ){ |
64
|
1
|
|
|
|
|
10
|
Error("'$FakeModulePath' have no constructor. If you like to create a mock of a package without constructor please use it without parameter list"); |
65
|
|
|
|
|
|
|
}else{ |
66
|
26
|
|
|
|
|
69
|
$self->{'MockStaticModule'} = 1; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
} |
69
|
113
|
100
|
|
|
|
272
|
my $FakeClass = $aFakeParams ? $FakeModulePath->$AlternativeConstructorName( @{$aFakeParams} ) : $FakeModulePath; |
|
80
|
|
|
|
|
262
|
|
70
|
113
|
|
|
|
|
1041
|
$self->_mockedModulePath($FakeModulePath); |
71
|
113
|
|
|
|
|
441
|
$self->_mockedSelf(Test::MockObject::Extends->new( $FakeClass )); |
72
|
113
|
|
|
|
|
297
|
$self->_initMockedModule(); |
73
|
|
|
|
|
|
|
|
74
|
113
|
|
|
|
|
276
|
return $self; |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
78
|
|
|
|
|
|
|
sub _mockedModulePath { |
79
|
273
|
|
|
273
|
|
355
|
my $self = shift; |
80
|
273
|
|
|
|
|
407
|
my ($ModulePath) = @_; |
81
|
273
|
100
|
|
|
|
924
|
return $self->{'MockedModulePath'} unless ($ModulePath); |
82
|
113
|
|
|
|
|
274
|
$self->{'MockedModulePath'} = $ModulePath; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
85
|
|
|
|
|
|
|
sub _mockedSelf { |
86
|
905
|
|
|
905
|
|
14903
|
my $self = shift; |
87
|
905
|
|
|
|
|
1242
|
my ($MockedSelf) = @_; |
88
|
905
|
100
|
|
|
|
3597
|
return $self->{'MockedModule'} unless ($MockedSelf); |
89
|
113
|
|
|
|
|
256
|
$self->{'MockedModule'} = $MockedSelf; |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
92
|
|
|
|
|
|
|
sub _initMockedModule { |
93
|
113
|
|
|
113
|
|
147
|
my $self = shift; |
94
|
|
|
|
|
|
|
|
95
|
113
|
|
|
|
|
404
|
$self->_mockedSelf()->{'__MethodCallCounter'} = Test::Mockify::MethodCallCounter->new(); |
96
|
113
|
|
|
|
|
236
|
$self->_mockedSelf()->{'__isMockified'} = 1; |
97
|
113
|
|
|
|
|
315
|
$self->_addGetParameterFromMockifyCall(); |
98
|
|
|
|
|
|
|
|
99
|
113
|
|
|
|
|
430
|
$self->{'__override'} = Sub::Override->new(); |
100
|
113
|
|
|
|
|
879
|
$self->_mockedSelf()->{'__override'} = $self->{'__override'}; |
101
|
113
|
|
|
|
|
220
|
$self->{'IsStaticMockStore'} = undef; |
102
|
113
|
|
|
|
|
173
|
$self->{'IsImportedMockStore'} = undef; |
103
|
113
|
|
|
|
|
150
|
return; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
107
|
|
|
|
|
|
|
=pod |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=head2 getMockObject |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Provides the actual mock object, which you can use in the test. |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
my $aParameterList = ['SomeValueForConstructor']; |
114
|
|
|
|
|
|
|
my $MockObjectBuilder = Test::Mockify->new( 'My::Module', $aParameterList ); |
115
|
|
|
|
|
|
|
my $MyModuleObject = $MockObjectBuilder->getMockObject(); |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=cut |
118
|
|
|
|
|
|
|
sub getMockObject { |
119
|
98
|
|
|
98
|
1
|
171
|
my $self = shift; |
120
|
98
|
|
|
|
|
220
|
return $self->_mockedSelf(); |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
#----------------------------------------------------------------------------------------= |
124
|
|
|
|
|
|
|
=pod |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=head2 mock |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
This is the place where the mocked methods are defined. The method also proves that the method you like to mock actually exists. |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=head3 synopsis |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
This method takes one parameter, which is the name of the method you like to mock. |
133
|
|
|
|
|
|
|
Because you need to specify more detailed the behaviour of this mock you have to chain the method signature (when) and the expected return value (then...). |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
For example, the next line will create a mocked version of the method log, but only if this method is called with any string and the number 123. In this case it will return the String 'Hello World'. Mockify will throw an error if this method is called somehow else. |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
my $MockObjectBuilder = Test::Mockify->new( 'Sample::Logger', [] ); |
138
|
|
|
|
|
|
|
$MockObjectBuilder->mock('log')->when(String(), Number(123))->thenReturn('Hello World'); |
139
|
|
|
|
|
|
|
my $SampleLogger = $MockObjectBuilder->getMockObject(); |
140
|
|
|
|
|
|
|
is($SampleLogger->log('abc',123), 'Hello World'); |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
=head4 when |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
To define the signature in the needed structure you must use the L. |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
=head4 whenAny |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
If you don't want to specify the method signature at all, you can use whenAny. |
150
|
|
|
|
|
|
|
It is not possible to mix C and C for the same method. |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=head4 then ... |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
For possible return types please look in L |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=cut |
157
|
|
|
|
|
|
|
sub mock { |
158
|
66
|
|
|
66
|
1
|
353
|
my $self = shift; |
159
|
66
|
|
|
|
|
124
|
my @Parameters = @_; |
160
|
|
|
|
|
|
|
|
161
|
66
|
|
|
|
|
103
|
my $ParameterAmount = scalar @Parameters; |
162
|
66
|
50
|
33
|
|
|
288
|
if($ParameterAmount == 1 && IsString($Parameters[0]) ){ |
163
|
66
|
|
|
|
|
130
|
$self->{'__UsedSubMock'} = 1; |
164
|
66
|
|
|
|
|
154
|
return $self->_addMockWithMethod($Parameters[0]); |
165
|
|
|
|
|
|
|
}else{ |
166
|
0
|
|
|
|
|
0
|
Error('"mock" Needs to be called with one Parameter which needs to be a String. '); |
167
|
|
|
|
|
|
|
} |
168
|
0
|
|
|
|
|
0
|
return; |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=pod |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=head2 spy |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
Use spy if you want to observe a method. You can use the L to ensure that the method was called with the expected parameters. |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=head3 synopsis |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
This method takes one parameter, which is the name of the method you like to spy. |
185
|
|
|
|
|
|
|
Because you need to specify more detailed the behaviour of this spy you have to define the method signature with C |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
For example, the next line will create a method spy of the method log, but only if this method is called with any string and the number 123. Mockify will throw an error if this method is called in another way. |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
my $MockObjectBuilder = Test::Mockify->new( 'Sample::Logger', [] ); |
190
|
|
|
|
|
|
|
$MockObjectBuilder->spy('log')->when(String(), Number(123)); |
191
|
|
|
|
|
|
|
my $SampleLogger = $MockObjectBuilder->getMockObject(); |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
# call spied method |
194
|
|
|
|
|
|
|
$SampleLogger->log('abc', 123); |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# verify that the spied method was called |
197
|
|
|
|
|
|
|
is_deeply(GetParametersFromMockifyCall($MockedLogger, 'log'),['abc', 123], 'Check parameters of first call'); |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=head4 when |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
To define the signature in the needed structure you must use the L. |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head4 whenAny |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
If you don't want to specify the method signature at all, you can use whenAny. |
206
|
|
|
|
|
|
|
It is not possible to mix C and C for the same method. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
=cut |
209
|
|
|
|
|
|
|
sub spy { |
210
|
16
|
|
|
16
|
1
|
78
|
my $self = shift; |
211
|
16
|
|
|
|
|
24
|
my ($MethodName) = @_; |
212
|
16
|
|
|
|
|
19
|
my $PointerOriginalMethod = \&{sprintf ('%s::%s', $self->_mockedModulePath(), $MethodName)}; |
|
16
|
|
|
|
|
27
|
|
213
|
|
|
|
|
|
|
#In order to have the current object available in the parameter list, it has to be injected here. |
214
|
|
|
|
|
|
|
return $self->_addMockWithMethodSpy($MethodName, sub { |
215
|
15
|
|
|
15
|
|
31
|
return $PointerOriginalMethod->($self->_mockedSelf(), @_); |
216
|
16
|
|
|
|
|
79
|
}); |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
219
|
|
|
|
|
|
|
sub _addMockWithMethod { |
220
|
97
|
|
|
97
|
|
146
|
my $self = shift; |
221
|
97
|
|
|
|
|
161
|
my ( $MethodName ) = @_; |
222
|
97
|
|
|
|
|
256
|
$self->_testMockTypeUsage($MethodName); |
223
|
96
|
100
|
|
|
|
285
|
if($self->{'IsStaticMockStore'}{$MethodName}){ |
|
|
100
|
|
|
|
|
|
224
|
18
|
|
|
|
|
99
|
return $self->_addStaticMock($MethodName, Test::Mockify::Method->new()); |
225
|
|
|
|
|
|
|
}elsif($self->{'IsImportedMockStore'}{$MethodName}){ |
226
|
13
|
|
|
|
|
61
|
return $self->_addImportedMock($MethodName, Test::Mockify::Method->new()); |
227
|
|
|
|
|
|
|
}else{ |
228
|
65
|
|
|
|
|
224
|
return $self->_addMock($MethodName, Test::Mockify::Method->new()); |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
232
|
|
|
|
|
|
|
sub _addMockWithMethodSpy { |
233
|
26
|
|
|
26
|
|
41
|
my $self = shift; |
234
|
26
|
|
|
|
|
47
|
my ( $MethodName, $PointerOriginalMethod ) = @_; |
235
|
26
|
|
|
|
|
64
|
$self->_testMockTypeUsage($MethodName); |
236
|
25
|
100
|
|
|
|
65
|
if($self->{'IsStaticMockStore'}{$MethodName}){ |
|
|
100
|
|
|
|
|
|
237
|
5
|
|
|
|
|
31
|
return $self->_addStaticMock($MethodName, Test::Mockify::MethodSpy->new($PointerOriginalMethod)); |
238
|
|
|
|
|
|
|
}elsif($self->{'IsImportedMockStore'}{$MethodName}){ |
239
|
5
|
|
|
|
|
29
|
return $self->_addImportedMock($MethodName, Test::Mockify::MethodSpy->new($PointerOriginalMethod)); |
240
|
|
|
|
|
|
|
}else{ |
241
|
15
|
|
|
|
|
47
|
return $self->_addMock($MethodName, Test::Mockify::MethodSpy->new($PointerOriginalMethod)); |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
#------------------------------------------------------------------------------------- |
245
|
|
|
|
|
|
|
sub _addMock { |
246
|
80
|
|
|
80
|
|
115
|
my $self = shift; |
247
|
80
|
|
|
|
|
148
|
my ($MethodName, $Method) = @_; |
248
|
|
|
|
|
|
|
|
249
|
80
|
|
|
|
|
145
|
ExistsMethod( $self->_mockedModulePath(), $MethodName ); |
250
|
79
|
|
|
|
|
160
|
$self->_mockedSelf()->{'__MethodCallCounter'}->addMethod( $MethodName ); |
251
|
79
|
100
|
|
|
|
236
|
if(not $self->{'MethodStore'}{$MethodName}){ |
252
|
69
|
100
|
|
|
|
145
|
if($self->{'MockStaticModule'}){ |
253
|
1
|
|
|
|
|
5
|
return $self->_addStaticMock($MethodName, Test::Mockify::Method->new()); |
254
|
|
|
|
|
|
|
}else{ |
255
|
68
|
|
33
|
|
|
283
|
$self->{'MethodStore'}{$MethodName} //= $Method; |
256
|
|
|
|
|
|
|
$self->_mockedSelf()->mock($MethodName, sub { |
257
|
82
|
|
|
82
|
|
6805
|
my $MockedSelf = shift; |
258
|
82
|
|
|
|
|
394
|
$MockedSelf->{'__MethodCallCounter'}->increment( $MethodName ); |
259
|
82
|
|
|
|
|
146
|
my @MockedParameters = @_; |
260
|
82
|
|
|
|
|
99
|
push @{$MockedSelf->{$MethodName.'_MockifyParams'}}, \@MockedParameters; |
|
82
|
|
|
|
|
255
|
|
261
|
82
|
100
|
|
|
|
193
|
my $WantAList = wantarray ? 1 : 0; |
262
|
82
|
|
|
|
|
178
|
return _callInjectedMethod($Method, \@MockedParameters, $WantAList, $MethodName); |
263
|
68
|
|
|
|
|
114
|
}); |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
} |
266
|
78
|
|
|
|
|
2323
|
return $self->{'MethodStore'}{$MethodName}; |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
269
|
|
|
|
|
|
|
sub _callInjectedMethod { |
270
|
|
|
|
|
|
|
# my $self = shift; #In Order to keep the mockify object out of the mocked method, I can't use the self. |
271
|
138
|
|
|
138
|
|
271
|
my ($Method, $aMockedParameters, $WantAList, $MethodName) = @_; |
272
|
138
|
|
|
|
|
177
|
my $ReturnValue; |
273
|
|
|
|
|
|
|
my @ReturnValue; |
274
|
138
|
|
|
|
|
186
|
eval { |
275
|
138
|
100
|
|
|
|
256
|
if($WantAList){ |
276
|
16
|
|
|
|
|
20
|
@ReturnValue = $Method->call(@{$aMockedParameters}); |
|
16
|
|
|
|
|
55
|
|
277
|
|
|
|
|
|
|
}else{ |
278
|
122
|
|
|
|
|
151
|
$ReturnValue = $Method->call(@{$aMockedParameters}); |
|
122
|
|
|
|
|
334
|
|
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
}; |
281
|
|
|
|
|
|
|
# $@ -> current error |
282
|
138
|
100
|
|
|
|
460
|
if ($@) { |
283
|
19
|
|
|
|
|
109
|
Error("\nError when calling method '$MethodName'\n".$@) |
284
|
|
|
|
|
|
|
} |
285
|
119
|
100
|
|
|
|
224
|
if($WantAList){ |
286
|
16
|
|
|
|
|
93
|
return @ReturnValue; |
287
|
|
|
|
|
|
|
}else{ |
288
|
103
|
|
|
|
|
473
|
return $ReturnValue; |
289
|
|
|
|
|
|
|
} |
290
|
0
|
|
|
|
|
0
|
return; |
291
|
|
|
|
|
|
|
} |
292
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
293
|
|
|
|
|
|
|
sub _buildMockSub{ |
294
|
38
|
|
|
38
|
|
59
|
my $self = shift; |
295
|
38
|
|
|
|
|
80
|
my ($MockedSelf, $MethodName, $Method) = @_; |
296
|
|
|
|
|
|
|
# The Sub::Override lexical scope don't get released if there is any Mockify var is pointing to it. |
297
|
|
|
|
|
|
|
# So the $MockedSelf needs to be resolved outside of the sub lexical scope. |
298
|
38
|
|
|
|
|
66
|
my $MethodCallCounter = \$MockedSelf->{'__MethodCallCounter'}; |
299
|
38
|
|
|
|
|
110
|
my $MockifyParamsStore = \$MockedSelf->{$MethodName.'_MockifyParams'}; |
300
|
38
|
|
|
|
|
59
|
my $MustUseShift = $self->{'__UsedSubMock'}; |
301
|
|
|
|
|
|
|
return sub { |
302
|
56
|
100
|
|
56
|
|
158
|
shift @_ if($MustUseShift); |
303
|
56
|
|
|
|
|
108
|
${$MethodCallCounter}->increment( $MethodName ); |
|
56
|
|
|
|
|
249
|
|
304
|
56
|
|
|
|
|
111
|
my @MockedParameters = @_; |
305
|
56
|
|
|
|
|
64
|
push( @{${$MockifyParamsStore}}, \@MockedParameters ); |
|
56
|
|
|
|
|
89
|
|
|
56
|
|
|
|
|
178
|
|
306
|
56
|
100
|
|
|
|
128
|
my $WantAList = wantarray ? 1 : 0; |
307
|
56
|
|
|
|
|
120
|
return _callInjectedMethod($Method, \@MockedParameters, $WantAList, $MethodName); |
308
|
38
|
|
|
|
|
170
|
}; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
311
|
|
|
|
|
|
|
sub _addStaticMock { |
312
|
24
|
|
|
24
|
|
37
|
my $self = shift; |
313
|
24
|
|
|
|
|
48
|
my ( $MethodName, $Method) = @_; |
314
|
|
|
|
|
|
|
|
315
|
24
|
|
|
|
|
55
|
ExistsMethod( $self->_mockedModulePath(), $MethodName ); |
316
|
24
|
|
|
|
|
50
|
$self->_mockedSelf()->{'__MethodCallCounter'}->addMethod( $MethodName ); |
317
|
24
|
100
|
|
|
|
74
|
if(not $self->{'MethodStore'}{$MethodName}){ |
318
|
22
|
|
|
|
|
44
|
$self->{'MethodStore'}{$MethodName} = $Method; |
319
|
22
|
|
|
|
|
53
|
my $MockedSelf = $self->_mockedSelf(); |
320
|
22
|
|
|
|
|
88
|
my $MockedMethodBody = $self->_buildMockSub($MockedSelf, $MethodName, $Method); |
321
|
22
|
100
|
|
|
|
167
|
if(!($MethodName =~ qr/::/sm)){ |
322
|
1
|
|
|
|
|
6
|
$self->_overrideInternalFunction($MethodName, $MockedMethodBody); |
323
|
|
|
|
|
|
|
}else{ |
324
|
21
|
|
|
|
|
79
|
$self->_overrideExternalFunction($MethodName, $MockedMethodBody); |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
} |
327
|
24
|
|
|
|
|
148
|
return $self->{'MethodStore'}{$MethodName}; |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
330
|
|
|
|
|
|
|
sub _overrideInternalFunction { |
331
|
1
|
|
|
1
|
|
3
|
my $self = shift; |
332
|
1
|
|
|
|
|
4
|
my ($MethodName, $MockedMethodBody) = @_; |
333
|
|
|
|
|
|
|
|
334
|
1
|
|
|
|
|
4
|
my $FullyQualifiedMethodName = sprintf('%s::%s', $self->_mockedModulePath(), $MethodName); |
335
|
1
|
|
|
|
|
5
|
$self->_replaceWithPrototype($FullyQualifiedMethodName, $MockedMethodBody); |
336
|
|
|
|
|
|
|
|
337
|
1
|
|
|
|
|
2
|
return; |
338
|
|
|
|
|
|
|
} |
339
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
340
|
|
|
|
|
|
|
sub _overrideExternalFunction { |
341
|
21
|
|
|
21
|
|
44
|
my $self = shift; |
342
|
21
|
|
|
|
|
37
|
my ($FullyQualifiedMethodName, $MockedMethodBody) = @_; |
343
|
21
|
|
|
|
|
62
|
$self->_replaceWithPrototype($FullyQualifiedMethodName, $MockedMethodBody); |
344
|
21
|
|
|
|
|
37
|
return; |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
347
|
|
|
|
|
|
|
sub _replaceWithPrototype { |
348
|
38
|
|
|
38
|
|
50
|
my $self = shift; |
349
|
38
|
|
|
|
|
62
|
my ($FullyQualifiedMethodName, $Sub) = @_; |
350
|
38
|
50
|
|
|
|
121
|
Error ('not a code ref ') unless ref $Sub eq 'CODE'; |
351
|
38
|
|
|
|
|
126
|
my $Prototype = prototype($FullyQualifiedMethodName); |
352
|
|
|
|
|
|
|
|
353
|
38
|
100
|
|
|
|
90
|
if(defined $Prototype){ |
354
|
33
|
|
|
|
|
2500
|
my $SubWithPrototype = eval( 'return sub ('. $Prototype .') {$Sub->(@_)}'); ## no critic (ProhibitStringyEval RequireInterpolationOfMetachars ) This is the only dynamic way to add the prototype |
355
|
33
|
50
|
|
|
|
105
|
Error($@, {'Error in eval, line' => __LINE__ - 1}) if($@); # Rethrow error if something went wrong in the eval. (For debugging) |
356
|
33
|
|
|
|
|
137
|
$self->{'__override'}->replace($FullyQualifiedMethodName, $SubWithPrototype); |
357
|
33
|
|
|
|
|
1395
|
return; |
358
|
|
|
|
|
|
|
} |
359
|
5
|
|
|
|
|
19
|
$self->{'__override'}->replace($FullyQualifiedMethodName, $Sub); |
360
|
5
|
|
|
|
|
233
|
return; |
361
|
|
|
|
|
|
|
} |
362
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
363
|
|
|
|
|
|
|
sub _addImportedMock { |
364
|
18
|
|
|
18
|
|
31
|
my $self = shift; |
365
|
18
|
|
|
|
|
33
|
my ( $MethodName, $Method) = @_; |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
ExistsMethod( |
368
|
|
|
|
|
|
|
$self->{'IsImportedMockStore'}{$MethodName}->{'Path'}, |
369
|
18
|
|
|
|
|
55
|
$self->{'IsImportedMockStore'}{$MethodName}->{'MethodName'}, |
370
|
|
|
|
|
|
|
{'Mock Imported In' => $self->_mockedModulePath()} |
371
|
|
|
|
|
|
|
); |
372
|
|
|
|
|
|
|
|
373
|
18
|
|
|
|
|
48
|
$self->_mockedSelf()->{'__MethodCallCounter'}->addMethod( $MethodName ); |
374
|
18
|
100
|
|
|
|
66
|
if(not $self->{'MethodStore'}{$MethodName}){ |
375
|
16
|
|
|
|
|
40
|
$self->{'MethodStore'}{$MethodName} = $Method; |
376
|
16
|
|
|
|
|
44
|
my $MockedSelf = $self->_mockedSelf(); |
377
|
16
|
|
|
|
|
64
|
my $MockedMethodBody = $self->_buildMockSub($MockedSelf, $MethodName, $Method); |
378
|
16
|
|
|
|
|
43
|
my $FullyQualifiedMethodName = sprintf ('%s::%s', $self->_mockedModulePath(), $self->{'IsImportedMockStore'}{$MethodName}->{'MethodName'}); |
379
|
16
|
|
|
|
|
56
|
$self->_replaceWithPrototype( $FullyQualifiedMethodName, $MockedMethodBody ); |
380
|
|
|
|
|
|
|
} |
381
|
18
|
|
|
|
|
104
|
return $self->{'MethodStore'}{$MethodName}; |
382
|
|
|
|
|
|
|
} |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
385
|
|
|
|
|
|
|
sub _addGetParameterFromMockifyCall { |
386
|
113
|
|
|
113
|
|
153
|
my $self = shift; |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
$self->_mockedSelf()->mock('__getParametersFromMockifyCall', |
389
|
|
|
|
|
|
|
sub{ |
390
|
20
|
|
|
20
|
|
1514
|
my $MockedSelf = shift; |
391
|
20
|
|
|
|
|
40
|
my ( $MethodName, $Position ) = @_; |
392
|
|
|
|
|
|
|
|
393
|
20
|
|
|
|
|
81
|
my $aParametersFromAllCalls = $MockedSelf->{$MethodName.'_MockifyParams'}; |
394
|
20
|
100
|
|
|
|
74
|
if( ref $aParametersFromAllCalls ne 'ARRAY' ){ |
395
|
1
|
|
|
|
|
5
|
Error( "$MethodName was not called" ); |
396
|
|
|
|
|
|
|
} |
397
|
19
|
100
|
|
|
|
27
|
if( scalar @{$aParametersFromAllCalls} < $Position ) { |
|
19
|
|
|
|
|
49
|
|
398
|
1
|
|
|
|
|
17
|
Error( "$MethodName was not called ".( $Position+1 ).' times',{ |
399
|
|
|
|
|
|
|
'Method' => "$MethodName", |
400
|
|
|
|
|
|
|
'Postion' => $Position, |
401
|
|
|
|
|
|
|
} ); |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
else { |
404
|
18
|
|
|
|
|
54
|
my $ParameterFromMockifyCall = $MockedSelf->{$MethodName.'_MockifyParams'}[$Position]; |
405
|
18
|
|
|
|
|
78
|
return $ParameterFromMockifyCall; |
406
|
|
|
|
|
|
|
} |
407
|
0
|
|
|
|
|
0
|
return; |
408
|
|
|
|
|
|
|
} |
409
|
113
|
|
|
|
|
191
|
); |
410
|
|
|
|
|
|
|
|
411
|
113
|
|
|
|
|
3948
|
return; |
412
|
|
|
|
|
|
|
} |
413
|
|
|
|
|
|
|
#---------------------------------------------------------------------------------------- |
414
|
|
|
|
|
|
|
sub _testMockTypeUsage { |
415
|
123
|
|
|
123
|
|
148
|
my $self = shift; |
416
|
123
|
|
|
|
|
182
|
my ($MethodName) = @_; |
417
|
123
|
|
|
|
|
177
|
my $PositionInCallerStack = 2; |
418
|
123
|
|
|
|
|
515
|
my $MethodMockType = (caller($PositionInCallerStack))[3]; # autodetect mock type (spy or mock) |
419
|
123
|
100
|
100
|
|
|
1055
|
if($self->{'MethodMockType'}{$MethodName} && $self->{'MethodMockType'}{$MethodName} ne $MethodMockType){ |
420
|
2
|
|
|
|
|
6
|
Error('It is not possible to mix spy and mock'); |
421
|
|
|
|
|
|
|
}else{ |
422
|
121
|
|
|
|
|
262
|
$self->{'MethodMockType'}{$MethodName} = $MethodMockType; |
423
|
|
|
|
|
|
|
} |
424
|
121
|
|
|
|
|
182
|
return; |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
1; |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
__END__ |