flexmock 2.2.1 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +4 -3
- data/README.md +7 -0
- data/lib/flexmock/core.rb +6 -2
- data/lib/flexmock/expectation.rb +33 -0
- data/lib/flexmock/expectation_director.rb +3 -1
- data/lib/flexmock/partial_mock.rb +21 -3
- data/lib/flexmock/validators.rb +145 -0
- data/lib/flexmock/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 471129d58809ac2dc9a53b22254f76e352f5b253
|
4
|
+
data.tar.gz: 3ac4914a786029b8cb1c10750b28f7de602c984b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dcab0eed825f701d642cce8286b9816b087b765beff23fe39205841e1289ecdfc80284a14d59cbbfb90a27d5050c50b25618fd5f27a6f9db4367e2b0b739360c
|
7
|
+
data.tar.gz: 8e97cc8946bb2fac86b6b69cb01dec2c42a2cb3aa67d0307dc696e94228bccd66f82cabe1a8c6bdb92b096ba91b3a6889b898e40af714cbc7c7d071b0496a4aa
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -22,6 +22,13 @@ Only significant changes (new APIs, deprecated APIs or backward-compatible
|
|
22
22
|
changes) are documented here, a.k.a. minor or major version bumps. If you want a
|
23
23
|
detailed changelog, go over the commit log in github (it's pretty low-traffic)
|
24
24
|
|
25
|
+
2.3.0:
|
26
|
+
- implemented validation of call arity for partial mocks. By setting
|
27
|
+
FlexMock.partials_verify_signatures = true
|
28
|
+
flexmock will verify on partials that the number of arguments, and the
|
29
|
+
keyword arguments passed to the mocked call match the existing method's
|
30
|
+
signature
|
31
|
+
|
25
32
|
2.2.0:
|
26
33
|
|
27
34
|
- #new_instances now mocks the #initialize method instead of mocking after the
|
data/lib/flexmock/core.rb
CHANGED
@@ -49,8 +49,10 @@ class FlexMock
|
|
49
49
|
|
50
50
|
class << self
|
51
51
|
attr_accessor :partials_are_based
|
52
|
+
attr_accessor :partials_verify_signatures
|
52
53
|
end
|
53
54
|
self.partials_are_based = false
|
55
|
+
self.partials_verify_signatures = false
|
54
56
|
|
55
57
|
# Create a FlexMock object with the given name. The name is used in
|
56
58
|
# error messages. If no container is given, create a new, one-off
|
@@ -153,8 +155,10 @@ class FlexMock
|
|
153
155
|
def flexmock_based_on(base_class)
|
154
156
|
@base_class = base_class
|
155
157
|
if base_class <= Kernel
|
156
|
-
|
157
|
-
|
158
|
+
if self.class != base_class
|
159
|
+
should_receive(:class => base_class)
|
160
|
+
should_receive(:kind_of?).and_return { |against| base_class <= against }
|
161
|
+
end
|
158
162
|
end
|
159
163
|
end
|
160
164
|
|
data/lib/flexmock/expectation.rb
CHANGED
@@ -38,6 +38,7 @@ class FlexMock
|
|
38
38
|
@location = location
|
39
39
|
@expected_args = nil
|
40
40
|
@count_validators = []
|
41
|
+
@signature_validator = SignatureValidator.new(self)
|
41
42
|
@count_validator_class = ExactCountValidator
|
42
43
|
@actual_count = 0
|
43
44
|
@return_value = nil
|
@@ -65,6 +66,9 @@ class FlexMock
|
|
65
66
|
@count_validators.each do |validator|
|
66
67
|
result << validator.describe
|
67
68
|
end
|
69
|
+
if !@signature_validator.null?
|
70
|
+
result << @signature_validator.describe
|
71
|
+
end
|
68
72
|
result
|
69
73
|
end
|
70
74
|
|
@@ -79,11 +83,18 @@ class FlexMock
|
|
79
83
|
FlexMock.framework_adapter.check(e.message) { false }
|
80
84
|
end
|
81
85
|
|
86
|
+
def validate_signature(args)
|
87
|
+
@signature_validator.validate(args)
|
88
|
+
rescue SignatureValidator::ValidationFailed => e
|
89
|
+
FlexMock.framework_adapter.check(e.message) { false }
|
90
|
+
end
|
91
|
+
|
82
92
|
# Verify the current call with the given arguments matches the
|
83
93
|
# expectations recorded in this object.
|
84
94
|
def verify_call(*args)
|
85
95
|
validate_eligible
|
86
96
|
validate_order
|
97
|
+
validate_signature(args)
|
87
98
|
@actual_count += 1
|
88
99
|
perform_yielding(args)
|
89
100
|
return_value(args)
|
@@ -186,6 +197,28 @@ class FlexMock
|
|
186
197
|
self
|
187
198
|
end
|
188
199
|
|
200
|
+
# Validate general parameters on the call signature
|
201
|
+
def with_signature(
|
202
|
+
required_arguments: 0, optional_arguments: 0, splat: false,
|
203
|
+
required_keyword_arguments: [], optional_keyword_arguments: [], keyword_splat: false)
|
204
|
+
@signature_validator = SignatureValidator.new(
|
205
|
+
self,
|
206
|
+
required_arguments: required_arguments,
|
207
|
+
optional_arguments: optional_arguments,
|
208
|
+
splat: splat,
|
209
|
+
required_keyword_arguments: required_keyword_arguments,
|
210
|
+
optional_keyword_arguments: optional_keyword_arguments,
|
211
|
+
keyword_splat: keyword_splat)
|
212
|
+
self
|
213
|
+
end
|
214
|
+
|
215
|
+
# Validate that the passed arguments match the method signature from the
|
216
|
+
# given instance method
|
217
|
+
def with_signature_matching(instance_method)
|
218
|
+
@signature_validator = SignatureValidator.from_instance_method(self, instance_method)
|
219
|
+
self
|
220
|
+
end
|
221
|
+
|
189
222
|
# :call-seq:
|
190
223
|
# and_return(value)
|
191
224
|
# and_return(value, value, ...)
|
@@ -40,7 +40,9 @@ class FlexMock
|
|
40
40
|
call_record.expectation = exp if call_record
|
41
41
|
FlexMock.check(
|
42
42
|
proc { "no matching handler found for " +
|
43
|
-
FlexMock.format_call(@sym, args)
|
43
|
+
FlexMock.format_call(@sym, args) +
|
44
|
+
"\nDefined expectations:\n " +
|
45
|
+
@expectations.map(&:description).join("\n ") }
|
44
46
|
) { !exp.nil? }
|
45
47
|
returned_value = exp.verify_call(*args)
|
46
48
|
returned_value
|
@@ -58,7 +58,8 @@ class FlexMock
|
|
58
58
|
:should_receive, :new_instances,
|
59
59
|
:should_receive_with_location,
|
60
60
|
:flexmock_get, :flexmock_teardown, :flexmock_verify,
|
61
|
-
:flexmock_received?, :flexmock_calls, :flexmock_find_expectation
|
61
|
+
:flexmock_received?, :flexmock_calls, :flexmock_find_expectation,
|
62
|
+
:invoke_original
|
62
63
|
]
|
63
64
|
|
64
65
|
# Initialize a PartialMockProxy object.
|
@@ -108,12 +109,27 @@ class FlexMock
|
|
108
109
|
flexmock_define_expectation(caller, *args)
|
109
110
|
end
|
110
111
|
|
112
|
+
# Invoke the original of a mocked method
|
113
|
+
#
|
114
|
+
# Usually called in a #and_return statement
|
115
|
+
def invoke_original(m, *args, &block)
|
116
|
+
if block
|
117
|
+
args << block
|
118
|
+
end
|
119
|
+
flexmock_invoke_original(m, args)
|
120
|
+
end
|
121
|
+
|
111
122
|
def flexmock_define_expectation(location, *args)
|
112
123
|
EXP_BUILDER.parse_should_args(self, args) do |method_name|
|
113
|
-
|
124
|
+
if !@methods_proxied.include?(method_name)
|
114
125
|
hide_existing_method(method_name)
|
115
126
|
end
|
116
127
|
ex = @mock.flexmock_define_expectation(location, method_name)
|
128
|
+
if FlexMock.partials_verify_signatures
|
129
|
+
if existing_method = @method_definitions[method_name]
|
130
|
+
ex.with_signature_matching(existing_method)
|
131
|
+
end
|
132
|
+
end
|
117
133
|
ex.mock = self
|
118
134
|
ex
|
119
135
|
end
|
@@ -334,8 +350,9 @@ class FlexMock
|
|
334
350
|
# not a singleton, all we need to do is override it with our own
|
335
351
|
# singleton.
|
336
352
|
def hide_existing_method(method_name)
|
337
|
-
stow_existing_definition(method_name)
|
353
|
+
existing_method = stow_existing_definition(method_name)
|
338
354
|
define_proxy_method(method_name)
|
355
|
+
existing_method
|
339
356
|
end
|
340
357
|
|
341
358
|
# Stow the existing method definition so that it can be recovered
|
@@ -345,6 +362,7 @@ class FlexMock
|
|
345
362
|
@method_definitions[method_name] = target_class_eval { instance_method(method_name) }
|
346
363
|
@methods_proxied << method_name
|
347
364
|
end
|
365
|
+
@method_definitions[method_name]
|
348
366
|
rescue NameError
|
349
367
|
end
|
350
368
|
|
data/lib/flexmock/validators.rb
CHANGED
@@ -9,6 +9,7 @@
|
|
9
9
|
# above copyright notice is included.
|
10
10
|
#+++
|
11
11
|
|
12
|
+
require 'set'
|
12
13
|
require 'flexmock/noop'
|
13
14
|
require 'flexmock/spy_describers'
|
14
15
|
|
@@ -136,4 +137,148 @@ class FlexMock
|
|
136
137
|
"At most #{@limit}"
|
137
138
|
end
|
138
139
|
end
|
140
|
+
|
141
|
+
# Validate that the call matches a given signature
|
142
|
+
#
|
143
|
+
# The validator created by {#initialize} matches any method call
|
144
|
+
class SignatureValidator
|
145
|
+
class ValidationFailed < RuntimeError
|
146
|
+
end
|
147
|
+
|
148
|
+
# The number of required arguments
|
149
|
+
attr_reader :required_arguments
|
150
|
+
# The number of optional arguments
|
151
|
+
attr_reader :optional_arguments
|
152
|
+
# Whether there is a positional argument splat
|
153
|
+
def splat?
|
154
|
+
@splat
|
155
|
+
end
|
156
|
+
# The names of required keyword arguments
|
157
|
+
# @return [Set<Symbol>]
|
158
|
+
attr_reader :required_keyword_arguments
|
159
|
+
# The names of optional keyword arguments
|
160
|
+
# @return [Set<Symbol>]
|
161
|
+
attr_reader :optional_keyword_arguments
|
162
|
+
# Whether there is a splat for keyword arguments (double-star)
|
163
|
+
def keyword_splat?
|
164
|
+
@keyword_splat
|
165
|
+
end
|
166
|
+
|
167
|
+
# Whether this method may have keyword arguments
|
168
|
+
def expects_keyword_arguments?
|
169
|
+
keyword_splat? || !required_keyword_arguments.empty? || !optional_keyword_arguments.empty?
|
170
|
+
end
|
171
|
+
|
172
|
+
# Whether this method may have keyword arguments
|
173
|
+
def requires_keyword_arguments?
|
174
|
+
!required_keyword_arguments.empty?
|
175
|
+
end
|
176
|
+
|
177
|
+
def initialize(
|
178
|
+
expectation,
|
179
|
+
required_arguments: 0,
|
180
|
+
optional_arguments: 0,
|
181
|
+
splat: true,
|
182
|
+
required_keyword_arguments: [],
|
183
|
+
optional_keyword_arguments: [],
|
184
|
+
keyword_splat: true)
|
185
|
+
@exp = expectation
|
186
|
+
@required_arguments = required_arguments
|
187
|
+
@optional_arguments = optional_arguments
|
188
|
+
@required_keyword_arguments = required_keyword_arguments.to_set
|
189
|
+
@optional_keyword_arguments = optional_keyword_arguments.to_set
|
190
|
+
@splat = splat
|
191
|
+
@keyword_splat = keyword_splat
|
192
|
+
end
|
193
|
+
|
194
|
+
# Whether this tests anything
|
195
|
+
#
|
196
|
+
# It will return if this validator would validate any set of arguments
|
197
|
+
def null?
|
198
|
+
splat? && keyword_splat?
|
199
|
+
end
|
200
|
+
|
201
|
+
def describe
|
202
|
+
".with_signature(
|
203
|
+
required_arguments: #{self.required_arguments},
|
204
|
+
optional_arguments: #{self.optional_arguments},
|
205
|
+
required_keyword_arguments: #{self.required_keyword_arguments.to_a},
|
206
|
+
optional_keyword_arguments: #{self.optional_keyword_arguments.to_a},
|
207
|
+
splat: #{self.splat?},
|
208
|
+
keyword_splat: #{self.keyword_splat?})"
|
209
|
+
end
|
210
|
+
|
211
|
+
# Validates whether the given arguments match the expected signature
|
212
|
+
#
|
213
|
+
# @param [Array] args
|
214
|
+
# @raise ValidationFailed
|
215
|
+
def validate(args)
|
216
|
+
kw_args = Hash.new
|
217
|
+
if expects_keyword_arguments?
|
218
|
+
last_is_kw_hash =
|
219
|
+
begin
|
220
|
+
args.last.kind_of?(Hash)
|
221
|
+
rescue NoMethodError
|
222
|
+
false
|
223
|
+
end
|
224
|
+
|
225
|
+
if last_is_kw_hash
|
226
|
+
kw_args = args[-1]
|
227
|
+
args = args[0..-2]
|
228
|
+
elsif requires_keyword_arguments?
|
229
|
+
raise ValidationFailed, "#{@exp} expects keyword arguments but none were provided"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# There is currently no way to disambiguate "given a block" from "given a
|
234
|
+
# proc as last argument" ... give some leeway in this case
|
235
|
+
|
236
|
+
if required_arguments > args.size
|
237
|
+
raise ValidationFailed, "#{@exp} expects at least #{required_arguments} positional arguments but got only #{args.size}"
|
238
|
+
elsif !splat? && (required_arguments + optional_arguments) < args.size
|
239
|
+
if !args.last.kind_of?(Proc) || (required_arguments + optional_arguments) < args.size - 1
|
240
|
+
raise ValidationFailed, "#{@exp} expects at most #{required_arguments + optional_arguments} positional arguments but got #{args.size}"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
missing_keyword_arguments = required_keyword_arguments.
|
245
|
+
find_all { |k| !kw_args.has_key?(k) }
|
246
|
+
if !missing_keyword_arguments.empty?
|
247
|
+
raise ValidationFailed, "#{@exp} missing required keyword arguments #{missing_keyword_arguments.map(&:to_s).sort.join(", ")}"
|
248
|
+
end
|
249
|
+
if !keyword_splat?
|
250
|
+
kw_args.each_key do |k|
|
251
|
+
if !optional_keyword_arguments.include?(k) && !required_keyword_arguments.include?(k)
|
252
|
+
raise ValidationFailed, "#{@exp} given unexpected keyword argument #{k}"
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# Create a validator that represents the signature of an existing method
|
259
|
+
def self.from_instance_method(exp, instance_method)
|
260
|
+
required_arguments, optional_arguments, splat = 0, 0, false
|
261
|
+
required_keyword_arguments, optional_keyword_arguments, keyword_splat = Set.new, Set.new, false
|
262
|
+
instance_method.parameters.each do |type, name|
|
263
|
+
case type
|
264
|
+
when :req then required_arguments += 1
|
265
|
+
when :opt then optional_arguments += 1
|
266
|
+
when :rest then splat = true
|
267
|
+
when :keyreq then required_keyword_arguments << name
|
268
|
+
when :key then optional_keyword_arguments << name
|
269
|
+
when :keyrest then keyword_splat = true
|
270
|
+
when :block
|
271
|
+
else raise ArgumentError, "cannot interpret parameter type #{type}"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
new(exp,
|
275
|
+
required_arguments: required_arguments,
|
276
|
+
optional_arguments: optional_arguments,
|
277
|
+
splat: splat,
|
278
|
+
required_keyword_arguments: required_keyword_arguments,
|
279
|
+
optional_keyword_arguments: optional_keyword_arguments,
|
280
|
+
keyword_splat: keyword_splat)
|
281
|
+
end
|
282
|
+
end
|
139
283
|
end
|
284
|
+
|
data/lib/flexmock/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flexmock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jim Weirich
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-10-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|