flexmock 2.4.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +5 -1
- data/README.md +70 -6
- data/flexmock.gemspec +1 -1
- data/lib/flexmock/argument_matchers.rb +3 -15
- data/lib/flexmock/argument_matching.rb +31 -1
- data/lib/flexmock/argument_types.rb +0 -4
- data/lib/flexmock/call_record.rb +10 -5
- data/lib/flexmock/call_validator.rb +2 -2
- data/lib/flexmock/composite_expectation.rb +4 -4
- data/lib/flexmock/core.rb +21 -18
- data/lib/flexmock/core_class_methods.rb +30 -11
- data/lib/flexmock/expectation.rb +68 -87
- data/lib/flexmock/expectation_builder.rb +9 -3
- data/lib/flexmock/expectation_director.rb +10 -12
- data/lib/flexmock/expectation_recorder.rb +4 -4
- data/lib/flexmock/explicit_needed.rb +2 -2
- data/lib/flexmock/partial_mock.rb +53 -31
- data/lib/flexmock/spy_describers.rb +9 -9
- data/lib/flexmock/test_unit_assert_spy_called.rb +15 -8
- data/lib/flexmock/validators.rb +10 -57
- data/lib/flexmock/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97fdfec71cb9dd687df62403b26eb8d4d24e4b28ec7f35f5e2d5f3ac6ffed943
|
4
|
+
data.tar.gz: 8f250a12f731099db17e9fcea78b92e0b92ac523376a34e06b5295f3a17ec996
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7672fd90360b33327119a7f135a7db9d05c7b7d41e0dd5299396296e0b68c8bb5175eaa2cf8d334b9b8cc62f52a87af4fe8af8216a4e9283341c909eed5ca196
|
7
|
+
data.tar.gz: a2112c506ef7152ba13aef9f67cb9ab5d58a0b566b5f50e7db00e545ff04e663da129f8291109dc25f028b2150ca4a936d96ced1444cbb85b4298534179046dc
|
data/.github/workflows/test.yml
CHANGED
data/README.md
CHANGED
@@ -22,6 +22,54 @@ 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
|
+
3.0.0:
|
26
|
+
- Added keyword argument and explicit proc support. Keyword argument support
|
27
|
+
is Ruby 3.0+ only, getting the support to work on Ruby 2.7 deemed to be too
|
28
|
+
complicated. See the release notes for 2.4 below for information about how
|
29
|
+
migration can be done smoothly
|
30
|
+
- `with` now expects all expected keyword arguments to be explicitly given in a
|
31
|
+
natural way, for instance:
|
32
|
+
|
33
|
+
```
|
34
|
+
mock.should_receive(:m).with("some", "args", with: 42)
|
35
|
+
```
|
36
|
+
|
37
|
+
The values given to the arguments can be any matcher valid for the positional
|
38
|
+
arguments
|
39
|
+
- note that not giving any keyword arguments to `with` is interpreted as a
|
40
|
+
negative (no keyword arguments are expected), and will fail if some arguments
|
41
|
+
are given. Call `with_any_kw_args` after the `with` if you do not desire
|
42
|
+
validation of keyword arguments:
|
43
|
+
|
44
|
+
```
|
45
|
+
mock.should_receive(:m).with("some", "args").with_any_kw_args
|
46
|
+
```
|
47
|
+
|
48
|
+
- for more complex matches, pass a match object to the `with_kw_args` method.
|
49
|
+
For instance, to match only some keyword arguments, do
|
50
|
+
|
51
|
+
```
|
52
|
+
mock.should_receive(:m).with("some", "args").with_kw_args(hsh(with: 42))
|
53
|
+
```
|
54
|
+
|
55
|
+
- this release also makes matching procs explicit. Instead of passing Proc at
|
56
|
+
the end of `with` as in 2.x, call `with_block` or `with_no_block`. If neither
|
57
|
+
are called, flexmock won't validate either way (ignore whether or not a block
|
58
|
+
was given). The default is to not match blocks, that is working
|
59
|
+
- The default is to assume that blocks are optional (i.e. flexmock will match
|
60
|
+
either way). Nonetheless, the method `with_optional_block` is implemented
|
61
|
+
to help migration from flexmock 2.4.0 (but is a no-op).
|
62
|
+
|
63
|
+
2.4.0:
|
64
|
+
- forward-compatible implementation of `with_kw_args`, `with_any_kw_args`,
|
65
|
+
`with_block` and `with_no_block`. The objective of this release is to ensure
|
66
|
+
that any test changes needed to handle Ruby 3 (along with flexmock 3) can run
|
67
|
+
on ruby 2.7 and flexmock 2.4
|
68
|
+
- the default behavior of flexmock 2 regarding proc matching, that is that one
|
69
|
+
needs to match them explicitly, is unchanged. Use `with_optional_block` instead
|
70
|
+
of passing `optional_proc` to `with`, to match optionally (IMPORTANT
|
71
|
+
the explicit `with` methods that match blocks are called `block`, not `proc`)
|
72
|
+
|
25
73
|
2.3.0:
|
26
74
|
- implemented validation of call arity for partial mocks. By setting
|
27
75
|
FlexMock.partials_verify_signatures = true
|
@@ -546,12 +594,10 @@ determining whether a given expectation matches or not.
|
|
546
594
|
series. The last value will be repeatably returned if the number of
|
547
595
|
matching calls exceeds the number of values.
|
548
596
|
|
549
|
-
* <b>and_return { |<em
|
597
|
+
* <b>and_return { |<em>*args</em>, <em>**kw</em>, <em>&block</em>| <em>code</em> ... }</b>
|
550
598
|
|
551
599
|
Declares that the expected message will return the yielded value of
|
552
600
|
the block. The block will receive all the arguments in the message.
|
553
|
-
If the message was provided a block, it will be passed as the last
|
554
|
-
parameter of the block's argument list.
|
555
601
|
|
556
602
|
* <b>returns( ... )</b>
|
557
603
|
|
@@ -755,13 +801,15 @@ The following rules are used for argument matching:
|
|
755
801
|
|
756
802
|
will match any even integer.
|
757
803
|
|
758
|
-
*
|
759
|
-
|
804
|
+
* By default, flexmock will ignore given blocks, that is it will assume that
|
805
|
+
blocks are optional.
|
806
|
+
|
807
|
+
* If you wish to verify that a method call received a block, use `with_block`
|
760
808
|
|
761
809
|
Example:
|
762
810
|
|
763
811
|
```ruby
|
764
|
-
m.should_receive(:foo).with(Integer
|
812
|
+
m.should_receive(:foo).with(Integer).with_block.and_return(:got_block)
|
765
813
|
m.should_receive(:foo).with(Integer).and_return(:no_block)
|
766
814
|
```
|
767
815
|
|
@@ -772,6 +820,22 @@ The following rules are used for argument matching:
|
|
772
820
|
m.foo(1) => returns :no_block
|
773
821
|
```
|
774
822
|
|
823
|
+
* If you wish to verify that a method call does not receive a block, use `with_no_block`
|
824
|
+
|
825
|
+
Example:
|
826
|
+
|
827
|
+
```ruby
|
828
|
+
m.should_receive(:foo).with(Integer).with_no_block.and_return(:no_block)
|
829
|
+
m.should_receive(:foo).with(Integer).and_return(:got_block)
|
830
|
+
```
|
831
|
+
|
832
|
+
will cause the mock to return the following:
|
833
|
+
|
834
|
+
```ruby
|
835
|
+
m.foo(1) { } => returns :got_block
|
836
|
+
m.foo(1) => returns :no_block
|
837
|
+
```
|
838
|
+
|
775
839
|
### Creating Partial Mocks
|
776
840
|
|
777
841
|
Sometimes it is useful to mock the behavior of one or two methods in
|
data/flexmock.gemspec
CHANGED
@@ -62,6 +62,9 @@ class FlexMock
|
|
62
62
|
def ===(target)
|
63
63
|
@hash.all? { |k, v| target[k] == v }
|
64
64
|
end
|
65
|
+
def empty?
|
66
|
+
@hash.empty?
|
67
|
+
end
|
65
68
|
def inspect
|
66
69
|
"hsh(#{@hash.inspect})"
|
67
70
|
end
|
@@ -80,19 +83,4 @@ class FlexMock
|
|
80
83
|
"ducktype(#{@methods.map{|m| m.inspect}.join(',')})"
|
81
84
|
end
|
82
85
|
end
|
83
|
-
|
84
|
-
####################################################################
|
85
|
-
# Match objects that implement all the methods in +methods+.
|
86
|
-
class OptionalProcMatcher
|
87
|
-
def initialize
|
88
|
-
end
|
89
|
-
def ===(target)
|
90
|
-
ArgumentMatching.missing?(target) || Proc === target
|
91
|
-
end
|
92
|
-
def inspect
|
93
|
-
"optional_proc"
|
94
|
-
end
|
95
|
-
end
|
96
|
-
OPTIONAL_PROC_MATCHER = OptionalProcMatcher.new
|
97
|
-
|
98
86
|
end
|
@@ -4,7 +4,13 @@ class FlexMock
|
|
4
4
|
|
5
5
|
MISSING_ARG = Object.new
|
6
6
|
|
7
|
-
def all_match?(expected_args, actual_args)
|
7
|
+
def all_match?(expected_args, expected_kw, expected_block, actual_args, actual_kw, actual_block)
|
8
|
+
all_match_args?(expected_args, actual_args) &&
|
9
|
+
all_match_kw?(expected_kw, actual_kw) &&
|
10
|
+
all_match_block?(expected_block, actual_block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def all_match_args?(expected_args, actual_args)
|
8
14
|
return true if expected_args.nil?
|
9
15
|
return false if actual_args.size > expected_args.size
|
10
16
|
i = 0
|
@@ -19,6 +25,30 @@ class FlexMock
|
|
19
25
|
true
|
20
26
|
end
|
21
27
|
|
28
|
+
def all_match_kw?(expected_kw, actual_kw)
|
29
|
+
return true if expected_kw.nil?
|
30
|
+
return expected_kw === actual_kw if expected_kw.kind_of? HashMatcher
|
31
|
+
|
32
|
+
matched_expected_k = Set.new
|
33
|
+
actual_kw.each do |actual_k, actual_v|
|
34
|
+
found_match = expected_kw.find do |k, v|
|
35
|
+
match?(k, actual_k) && match?(v, actual_v)
|
36
|
+
end
|
37
|
+
return false unless found_match
|
38
|
+
matched_expected_k << found_match
|
39
|
+
end
|
40
|
+
|
41
|
+
return false unless matched_expected_k.size == expected_kw.keys.size
|
42
|
+
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
def all_match_block?(expected_block, actual_block)
|
47
|
+
return true if expected_block.nil?
|
48
|
+
|
49
|
+
!(expected_block ^ actual_block)
|
50
|
+
end
|
51
|
+
|
22
52
|
# Does the expected argument match the corresponding actual value.
|
23
53
|
def match?(expected, actual)
|
24
54
|
expected === actual ||
|
data/lib/flexmock/call_record.rb
CHANGED
@@ -11,10 +11,11 @@
|
|
11
11
|
|
12
12
|
class FlexMock
|
13
13
|
|
14
|
-
CallRecord = Struct.new(:method_name, :args, :
|
15
|
-
def matches?(sym,
|
14
|
+
CallRecord = Struct.new(:method_name, :args, :kw, :block, :expectation) do
|
15
|
+
def matches?(sym, expected_args, expected_kw, options)
|
16
16
|
method_name == sym &&
|
17
|
-
ArgumentMatching.
|
17
|
+
ArgumentMatching.all_match_args?(expected_args, args) &&
|
18
|
+
ArgumentMatching.all_match_kw?(expected_kw, kw) &&
|
18
19
|
matches_block?(options[:with_block])
|
19
20
|
end
|
20
21
|
|
@@ -22,8 +23,12 @@ class FlexMock
|
|
22
23
|
|
23
24
|
def matches_block?(block_option)
|
24
25
|
block_option.nil? ||
|
25
|
-
(block_option &&
|
26
|
-
(!block_option && !
|
26
|
+
(block_option && block) ||
|
27
|
+
(!block_option && !block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def block_given
|
31
|
+
block
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
@@ -24,10 +24,10 @@ class FlexMock
|
|
24
24
|
# * :on_count => n -- If given, the :and validations on only run on the
|
25
25
|
# nth invocation.
|
26
26
|
#
|
27
|
-
def received?(calls, method_name, args, options)
|
27
|
+
def received?(calls, method_name, args, kw, options)
|
28
28
|
count = 0
|
29
29
|
calls.each { |call_record|
|
30
|
-
if call_record.matches?(method_name, args, options)
|
30
|
+
if call_record.matches?(method_name, args, kw, options)
|
31
31
|
count += 1
|
32
32
|
run_additional_validations(call_record, count, options)
|
33
33
|
end
|
@@ -16,9 +16,9 @@ class FlexMock
|
|
16
16
|
end
|
17
17
|
|
18
18
|
# Apply the constraint method to all expectations in the composite.
|
19
|
-
def method_missing(sym, *args, &block)
|
19
|
+
def method_missing(sym, *args, **kw, &block)
|
20
20
|
@expectations.each do |expectation|
|
21
|
-
expectation.send(sym, *args, &block)
|
21
|
+
expectation.send(sym, *args, **kw, &block)
|
22
22
|
end
|
23
23
|
self
|
24
24
|
end
|
@@ -38,9 +38,9 @@ class FlexMock
|
|
38
38
|
|
39
39
|
# Start a new method expectation. The following constraints will be
|
40
40
|
# applied to the new expectation.
|
41
|
-
def should_receive(*args, &block)
|
41
|
+
def should_receive(*args, **kw, &block)
|
42
42
|
@expectations.first.mock.
|
43
|
-
flexmock_define_expectation(caller, *args, &block)
|
43
|
+
flexmock_define_expectation(caller, *args, **kw, &block)
|
44
44
|
end
|
45
45
|
|
46
46
|
# Return a string representations
|
data/lib/flexmock/core.rb
CHANGED
@@ -137,23 +137,22 @@ class FlexMock
|
|
137
137
|
end
|
138
138
|
|
139
139
|
# Handle missing methods by attempting to look up a handler.
|
140
|
-
def method_missing(sym, *args, &block)
|
140
|
+
def method_missing(sym, *args, **kw, &block)
|
141
141
|
FlexMock.verify_mocking_allowed!
|
142
142
|
|
143
|
-
|
144
|
-
call_record = CallRecord.new(sym, enhanced_args, block_given?)
|
143
|
+
call_record = CallRecord.new(sym, args, kw, block)
|
145
144
|
@calls << call_record
|
146
145
|
flexmock_wrap do
|
147
146
|
if flexmock_closed?
|
148
147
|
FlexMock.undefined
|
149
148
|
elsif exp = flexmock_expectations_for(sym)
|
150
|
-
exp.call(args, block, call_record)
|
149
|
+
exp.call(args, kw, block, call_record)
|
151
150
|
elsif @base_class && @base_class.flexmock_defined?(sym)
|
152
151
|
FlexMock.undefined
|
153
152
|
elsif @ignore_missing
|
154
153
|
FlexMock.undefined
|
155
154
|
else
|
156
|
-
super(sym, *args, &block)
|
155
|
+
super(sym, *args, **kw, &block)
|
157
156
|
end
|
158
157
|
end
|
159
158
|
end
|
@@ -167,9 +166,9 @@ class FlexMock
|
|
167
166
|
end
|
168
167
|
|
169
168
|
# Find the mock expectation for method sym and arguments.
|
170
|
-
def flexmock_find_expectation(method_name, *args) # :nodoc:
|
169
|
+
def flexmock_find_expectation(method_name, *args, **kw, &block) # :nodoc:
|
171
170
|
if exp = flexmock_expectations_for(method_name)
|
172
|
-
exp.find_expectation(
|
171
|
+
exp.find_expectation(args, kw, block)
|
173
172
|
end
|
174
173
|
end
|
175
174
|
|
@@ -195,8 +194,8 @@ class FlexMock
|
|
195
194
|
CALL_VALIDATOR = CallValidator.new
|
196
195
|
|
197
196
|
# True if the mock received the given method and arguments.
|
198
|
-
def flexmock_received?(method_name, args, options={})
|
199
|
-
CALL_VALIDATOR.received?(@calls, method_name, args, options)
|
197
|
+
def flexmock_received?(method_name, args, kw, options = {})
|
198
|
+
CALL_VALIDATOR.received?(@calls, method_name, args, kw, options)
|
200
199
|
end
|
201
200
|
|
202
201
|
# Return the list of calls made on this mock. Used in formatting
|
@@ -207,13 +206,17 @@ class FlexMock
|
|
207
206
|
|
208
207
|
# Invocke the original non-mocked functionality for the given
|
209
208
|
# symbol.
|
210
|
-
def flexmock_invoke_original(method_name, args,
|
209
|
+
def flexmock_invoke_original(method_name, args, kw = {})
|
211
210
|
return FlexMock.undefined
|
212
211
|
end
|
213
212
|
|
214
213
|
# Override the built-in +method+ to include the mocked methods.
|
215
214
|
def method(method_name)
|
216
|
-
flexmock_expectations_for(method_name)
|
215
|
+
if (expectations = flexmock_expectations_for(method_name))
|
216
|
+
->(*args, **kw, &block) { expectations.call(args, kw, block) }
|
217
|
+
else
|
218
|
+
super
|
219
|
+
end
|
217
220
|
rescue NameError => ex
|
218
221
|
if ignore_missing?
|
219
222
|
proc { FlexMock.undefined }
|
@@ -241,15 +244,15 @@ class FlexMock
|
|
241
244
|
#
|
242
245
|
# See Expectation for a list of declarators that can be used.
|
243
246
|
#
|
244
|
-
def should_receive(*args)
|
245
|
-
flexmock_define_expectation(caller, *args)
|
247
|
+
def should_receive(*args, **kw)
|
248
|
+
flexmock_define_expectation(caller, *args, **kw)
|
246
249
|
end
|
247
250
|
|
248
251
|
ON_RUBY_20 = (RUBY_VERSION =~ /^2\.0\./)
|
249
252
|
|
250
253
|
# Using +location+, define the expectations specified by +args+.
|
251
|
-
def flexmock_define_expectation(location, *args)
|
252
|
-
@last_expectation = EXP_BUILDER.parse_should_args(self, args) do |method_name|
|
254
|
+
def flexmock_define_expectation(location, *args, **kw)
|
255
|
+
@last_expectation = EXP_BUILDER.parse_should_args(self, args, kw) do |method_name|
|
253
256
|
exp = flexmock_expectations_for(method_name) || ExpectationDirector.new(method_name)
|
254
257
|
@expectations[method_name] = exp
|
255
258
|
result = Expectation.new(self, method_name, location)
|
@@ -258,7 +261,7 @@ class FlexMock
|
|
258
261
|
|
259
262
|
if @base_class && !@base_class.flexmock_defined?(method_name)
|
260
263
|
if !ON_RUBY_20 || !@base_class.ancestors.include?(Class)
|
261
|
-
result = ExplicitNeeded.new(result, method_name, @base_class)
|
264
|
+
result = ExplicitNeeded.new(result, method_name, @base_class)
|
262
265
|
end
|
263
266
|
end
|
264
267
|
result
|
@@ -298,8 +301,8 @@ class FlexMock
|
|
298
301
|
# to explicitly invoke the method_missing logic.
|
299
302
|
def override_existing_method(method_name)
|
300
303
|
sclass.class_eval <<-EOS
|
301
|
-
def #{method_name}(*args, &block)
|
302
|
-
method_missing(:#{method_name}, *args, &block)
|
304
|
+
def #{method_name}(*args, **kw, &block)
|
305
|
+
method_missing(:#{method_name}, *args, **kw, &block)
|
303
306
|
end
|
304
307
|
EOS
|
305
308
|
end
|
@@ -78,23 +78,42 @@ class FlexMock
|
|
78
78
|
|
79
79
|
# Class method to format a method name and argument list as a nice
|
80
80
|
# looking string.
|
81
|
-
def format_call(sym, args) # :nodoc:
|
82
|
-
"#{sym}(#{format_args(args)})"
|
81
|
+
def format_call(sym, args, kw) # :nodoc:
|
82
|
+
"#{sym}(#{format_args(args, kw)})"
|
83
83
|
end
|
84
84
|
|
85
85
|
# Class method to format a list of args (the part between the
|
86
86
|
# parenthesis).
|
87
|
-
def format_args(args)
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
87
|
+
def format_args(args, kw)
|
88
|
+
args =
|
89
|
+
if args
|
90
|
+
args = args.map do |a|
|
91
|
+
FlexMock.forbid_mocking("<recursive call to mocked method in #inspect>") do
|
92
|
+
a.inspect
|
93
|
+
end
|
92
94
|
end
|
95
|
+
args.join(', ')
|
96
|
+
else
|
97
|
+
"*args"
|
93
98
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
99
|
+
|
100
|
+
kw =
|
101
|
+
if kw.kind_of? HashMatcher
|
102
|
+
kw.inspect
|
103
|
+
elsif kw && !kw.empty?
|
104
|
+
kw = kw.transform_values do |v|
|
105
|
+
FlexMock.forbid_mocking("<recursive call to mocked method in #inspect>") do
|
106
|
+
v.inspect
|
107
|
+
end
|
108
|
+
end
|
109
|
+
kw.map { |k, v| "#{k}: #{v}" }.join(', ')
|
110
|
+
elsif kw.nil?
|
111
|
+
# Don't append **kwargs to signature if ruby version < 3
|
112
|
+
# in order to not break existing code still on ruby2
|
113
|
+
"**kwargs" unless RUBY_VERSION < "3"
|
114
|
+
end
|
115
|
+
|
116
|
+
[(args unless args.empty?), kw].compact.join(", ")
|
98
117
|
end
|
99
118
|
|
100
119
|
# Check will assert the block returns true. If it doesn't, an
|
data/lib/flexmock/expectation.rb
CHANGED
@@ -37,11 +37,11 @@ class FlexMock
|
|
37
37
|
@sym = sym
|
38
38
|
@location = location
|
39
39
|
@expected_args = nil
|
40
|
-
@
|
41
|
-
@expected_block = nil
|
40
|
+
@expected_kw = nil
|
42
41
|
@count_validators = []
|
43
42
|
@signature_validator = SignatureValidator.new(self)
|
44
43
|
@count_validator_class = ExactCountValidator
|
44
|
+
@with_block = nil
|
45
45
|
@actual_count = 0
|
46
46
|
@return_value = nil
|
47
47
|
@return_queue = []
|
@@ -52,7 +52,7 @@ class FlexMock
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def to_s
|
55
|
-
FlexMock.format_call(@sym, @expected_args)
|
55
|
+
FlexMock.format_call(@sym, @expected_args, @expected_kw)
|
56
56
|
end
|
57
57
|
|
58
58
|
# Return a description of the matching features of the
|
@@ -64,7 +64,9 @@ class FlexMock
|
|
64
64
|
#
|
65
65
|
def description
|
66
66
|
result = ["should_receive(#{@sym.inspect})"]
|
67
|
-
|
67
|
+
if @expected_args || @expected_kw
|
68
|
+
result << ".with(#{FlexMock.format_args(@expected_args, @expected_kw)})"
|
69
|
+
end
|
68
70
|
@count_validators.each do |validator|
|
69
71
|
result << validator.describe
|
70
72
|
end
|
@@ -85,63 +87,49 @@ class FlexMock
|
|
85
87
|
FlexMock.framework_adapter.check(e.message) { false }
|
86
88
|
end
|
87
89
|
|
88
|
-
def validate_signature(args)
|
89
|
-
@signature_validator.validate(args)
|
90
|
+
def validate_signature(args, kw, block)
|
91
|
+
@signature_validator.validate(args, kw, block)
|
90
92
|
rescue SignatureValidator::ValidationFailed => e
|
91
93
|
FlexMock.framework_adapter.check(e.message) { false }
|
92
94
|
end
|
93
95
|
|
94
96
|
# Verify the current call with the given arguments matches the
|
95
97
|
# expectations recorded in this object.
|
96
|
-
def verify_call(
|
98
|
+
def verify_call(args, kw, block)
|
97
99
|
validate_eligible
|
98
100
|
validate_order
|
99
|
-
|
100
|
-
if block
|
101
|
-
args + [block]
|
102
|
-
else
|
103
|
-
args
|
104
|
-
end
|
105
|
-
|
106
|
-
validate_signature(enhanced_args)
|
101
|
+
validate_signature(args, kw, block)
|
107
102
|
@actual_count += 1
|
108
|
-
perform_yielding(
|
109
|
-
return_value(args, block)
|
103
|
+
perform_yielding(block)
|
104
|
+
return_value(args, kw, block)
|
110
105
|
end
|
111
106
|
|
112
107
|
# Public return value (odd name to avoid accidental use as a
|
113
108
|
# constraint).
|
114
|
-
def _return_value(args, block) # :nodoc:
|
115
|
-
return_value(args, block)
|
109
|
+
def _return_value(args, kw, block) # :nodoc:
|
110
|
+
return_value(args, kw, block)
|
116
111
|
end
|
117
112
|
|
118
113
|
# Find the return value for this expectation. (private version)
|
119
|
-
def return_value(args, block)
|
114
|
+
def return_value(args, kw, block)
|
120
115
|
case @return_queue.size
|
121
116
|
when 0
|
122
|
-
ret_block = lambda {
|
117
|
+
ret_block = lambda { |*, **| @return_value }
|
123
118
|
when 1
|
124
119
|
ret_block = @return_queue.first
|
125
120
|
else
|
126
121
|
ret_block = @return_queue.shift
|
127
122
|
end
|
128
|
-
|
129
|
-
if @expected_block
|
130
|
-
ret_block.call(*args, &block)
|
131
|
-
elsif block && @expected_block.nil?
|
132
|
-
ret_block.call(*args, block)
|
133
|
-
else
|
134
|
-
ret_block.call(*args)
|
135
|
-
end
|
123
|
+
ret_block.call(*args, **kw, &block)
|
136
124
|
end
|
137
125
|
private :return_value
|
138
126
|
|
139
127
|
# Yield stored values to any blocks given.
|
140
|
-
def perform_yielding(
|
128
|
+
def perform_yielding(block)
|
141
129
|
@return_value = nil
|
142
130
|
unless @yield_queue.empty?
|
143
131
|
values = (@yield_queue.size == 1) ? @yield_queue.first : @yield_queue.shift
|
144
|
-
if block
|
132
|
+
if block && block.respond_to?(:call)
|
145
133
|
values.each do |v|
|
146
134
|
@return_value = block.call(*v)
|
147
135
|
end
|
@@ -185,44 +173,14 @@ class FlexMock
|
|
185
173
|
|
186
174
|
# Does the argument list match this expectation's argument
|
187
175
|
# specification.
|
188
|
-
def match_args(args)
|
189
|
-
expected_args
|
190
|
-
if @expected_kw_args
|
191
|
-
if proc_matcher?(@expected_args&.last) && @expected_block.nil?
|
192
|
-
@expected_args[0..-2] + [@expected_kw_args, @expected_args[-1]]
|
193
|
-
else
|
194
|
-
(@expected_args || []) + [@expected_kw_args]
|
195
|
-
end
|
196
|
-
else
|
197
|
-
@expected_args
|
198
|
-
end
|
199
|
-
|
200
|
-
if @expected_block
|
201
|
-
expected_args = (expected_args || []) + [@expected_block]
|
202
|
-
end
|
203
|
-
|
204
|
-
ArgumentMatching.all_match?(expected_args, args)
|
205
|
-
end
|
206
|
-
|
207
|
-
def proc_matcher?(obj)
|
208
|
-
obj == Proc || obj == OPTIONAL_PROC_MATCHER
|
209
|
-
end
|
210
|
-
|
211
|
-
def with_optional_block
|
212
|
-
@expected_block = OPTIONAL_PROC_MATCHER
|
213
|
-
end
|
214
|
-
|
215
|
-
def with_block
|
216
|
-
@expected_block = Proc
|
217
|
-
end
|
218
|
-
|
219
|
-
def with_no_block
|
220
|
-
@expected_block = false
|
176
|
+
def match_args(args, kw, block)
|
177
|
+
ArgumentMatching.all_match?(@expected_args, @expected_kw, @expected_block, args, kw, block)
|
221
178
|
end
|
222
179
|
|
223
180
|
# Declare that the method should expect the given argument list.
|
224
|
-
def with(*args)
|
181
|
+
def with(*args, **kw)
|
225
182
|
@expected_args = args
|
183
|
+
@expected_kw = kw
|
226
184
|
self
|
227
185
|
end
|
228
186
|
|
@@ -235,42 +193,71 @@ class FlexMock
|
|
235
193
|
# arguments of any type.
|
236
194
|
def with_any_args
|
237
195
|
@expected_args = nil
|
196
|
+
@expected_kw = nil
|
238
197
|
self
|
239
198
|
end
|
240
199
|
|
241
200
|
# Declare that the method can be called with any number of
|
242
201
|
# arguments of any type.
|
243
|
-
def
|
244
|
-
@
|
202
|
+
def with_any_kw_args
|
203
|
+
@expected_kw = nil
|
245
204
|
self
|
246
205
|
end
|
247
206
|
|
248
207
|
# Declare that the method can be called with any number of
|
249
208
|
# arguments of any type.
|
250
|
-
def
|
251
|
-
@
|
209
|
+
def with_any_positional_args
|
210
|
+
@expected_args = nil
|
211
|
+
self
|
212
|
+
end
|
213
|
+
|
214
|
+
# Declare that the method should be called with the given
|
215
|
+
# keyword arguments
|
216
|
+
def with_kw_args(kw)
|
217
|
+
@expected_kw = kw
|
218
|
+
self
|
219
|
+
end
|
220
|
+
|
221
|
+
# Declare that the call should have a block
|
222
|
+
def with_block
|
223
|
+
@expected_block = true
|
224
|
+
self
|
225
|
+
end
|
226
|
+
|
227
|
+
# Declare that the call should have a block
|
228
|
+
def with_no_block
|
229
|
+
@expected_block = false
|
230
|
+
self
|
231
|
+
end
|
232
|
+
|
233
|
+
def with_optional_block
|
234
|
+
@expected_block = nil
|
252
235
|
self
|
253
236
|
end
|
254
237
|
|
255
238
|
# Validate general parameters on the call signature
|
256
239
|
def with_signature(
|
257
|
-
|
258
|
-
|
240
|
+
required_arguments: 0, optional_arguments: 0, splat: false,
|
241
|
+
required_keyword_arguments: [], optional_keyword_arguments: [],
|
242
|
+
keyword_splat: false
|
243
|
+
)
|
259
244
|
@signature_validator = SignatureValidator.new(
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
245
|
+
self,
|
246
|
+
required_arguments: required_arguments,
|
247
|
+
optional_arguments: optional_arguments,
|
248
|
+
splat: splat,
|
249
|
+
required_keyword_arguments: required_keyword_arguments,
|
250
|
+
optional_keyword_arguments: optional_keyword_arguments,
|
251
|
+
keyword_splat: keyword_splat
|
252
|
+
)
|
267
253
|
self
|
268
254
|
end
|
269
255
|
|
270
256
|
# Validate that the passed arguments match the method signature from the
|
271
257
|
# given instance method
|
272
258
|
def with_signature_matching(instance_method)
|
273
|
-
@signature_validator =
|
259
|
+
@signature_validator =
|
260
|
+
SignatureValidator.from_instance_method(self, instance_method)
|
274
261
|
self
|
275
262
|
end
|
276
263
|
|
@@ -405,15 +392,9 @@ class FlexMock
|
|
405
392
|
|
406
393
|
def pass_thru(&block)
|
407
394
|
block ||= lambda { |value| value }
|
408
|
-
and_return { |*args,
|
395
|
+
and_return { |*args, **kw|
|
409
396
|
begin
|
410
|
-
|
411
|
-
if Proc === args.last
|
412
|
-
orig_block = args.last
|
413
|
-
args = args[0..-2]
|
414
|
-
end
|
415
|
-
end
|
416
|
-
block.call(@mock.flexmock_invoke_original(@sym, args, orig_block))
|
397
|
+
block.call(@mock.flexmock_invoke_original(@sym, args, kw))
|
417
398
|
rescue NoMethodError => e
|
418
399
|
if e.name == @sym
|
419
400
|
raise e, "#{e.message} while performing #pass_thru in expectation object #{self}"
|
@@ -22,12 +22,12 @@ class FlexMock
|
|
22
22
|
# hashes, and identifies the method names specified by each. As each
|
23
23
|
# method name is identified, create a mock expectation for it using the
|
24
24
|
# supplied block.
|
25
|
-
def parse_should_args(mock, args, &block) # :nodoc:
|
25
|
+
def parse_should_args(mock, args, kw, &block) # :nodoc:
|
26
26
|
result = CompositeExpectation.new
|
27
27
|
args.each do |arg|
|
28
28
|
case arg
|
29
29
|
when Hash
|
30
|
-
arg.each do |k,v|
|
30
|
+
arg.each do |k, v|
|
31
31
|
exp = create_expectation(mock, k, &block).and_return(v)
|
32
32
|
result.add(exp)
|
33
33
|
end
|
@@ -35,6 +35,12 @@ class FlexMock
|
|
35
35
|
result.add(create_expectation(mock, arg, &block))
|
36
36
|
end
|
37
37
|
end
|
38
|
+
|
39
|
+
kw.each do |k, v|
|
40
|
+
exp = create_expectation(mock, k, &block).and_return(v)
|
41
|
+
result.add(exp)
|
42
|
+
end
|
43
|
+
|
38
44
|
result
|
39
45
|
end
|
40
46
|
|
@@ -86,7 +92,7 @@ class FlexMock
|
|
86
92
|
names.each do |name|
|
87
93
|
exp = mock.flexmock_find_expectation(name)
|
88
94
|
if exp
|
89
|
-
next_mock = exp._return_value([], nil)
|
95
|
+
next_mock = exp._return_value([], {}, nil)
|
90
96
|
check_proper_mock(next_mock, name)
|
91
97
|
else
|
92
98
|
next_mock = container.flexmock("demeter_#{name}")
|
@@ -35,18 +35,16 @@ class FlexMock
|
|
35
35
|
# but at least we will get a good failure message). Finally,
|
36
36
|
# check for expectations that don't have any argument matching
|
37
37
|
# criteria.
|
38
|
-
def call(args, block
|
39
|
-
|
40
|
-
find_args << block if block
|
41
|
-
exp = find_expectation(*find_args)
|
38
|
+
def call(args, kw, block, call_record=nil)
|
39
|
+
exp = find_expectation(args, kw, block)
|
42
40
|
call_record.expectation = exp if call_record
|
43
41
|
FlexMock.check(
|
44
42
|
proc { "no matching handler found for " +
|
45
|
-
FlexMock.format_call(@sym, args) +
|
43
|
+
FlexMock.format_call(@sym, args, kw) +
|
46
44
|
"\nDefined expectations:\n " +
|
47
45
|
@expectations.map(&:description).join("\n ") }
|
48
46
|
) { !exp.nil? }
|
49
|
-
returned_value = exp.verify_call(
|
47
|
+
returned_value = exp.verify_call(args, kw, block)
|
50
48
|
returned_value
|
51
49
|
end
|
52
50
|
|
@@ -56,11 +54,11 @@ class FlexMock
|
|
56
54
|
end
|
57
55
|
|
58
56
|
# Find an expectation matching the given arguments.
|
59
|
-
def find_expectation(
|
57
|
+
def find_expectation(args, kw, block) # :nodoc:
|
60
58
|
if @expectations.empty?
|
61
|
-
find_expectation_in(@defaults,
|
59
|
+
find_expectation_in(@defaults, args, kw, block)
|
62
60
|
else
|
63
|
-
find_expectation_in(@expectations,
|
61
|
+
find_expectation_in(@expectations, args, kw, block)
|
64
62
|
end
|
65
63
|
end
|
66
64
|
|
@@ -86,9 +84,9 @@ class FlexMock
|
|
86
84
|
|
87
85
|
private
|
88
86
|
|
89
|
-
def find_expectation_in(expectations,
|
90
|
-
expectations.find { |e| e.match_args(args) && e.eligible? } ||
|
91
|
-
expectations.find { |e| e.match_args(args) }
|
87
|
+
def find_expectation_in(expectations, args, kw, block)
|
88
|
+
expectations.find { |e| e.match_args(args, kw, block) && e.eligible? } ||
|
89
|
+
expectations.find { |e| e.match_args(args, kw, block) }
|
92
90
|
end
|
93
91
|
end
|
94
92
|
|
@@ -23,8 +23,8 @@ class FlexMock
|
|
23
23
|
end
|
24
24
|
|
25
25
|
# Save any incoming messages to be played back later.
|
26
|
-
def method_missing(sym, *args, &block)
|
27
|
-
@expectations << [sym, args, block]
|
26
|
+
def method_missing(sym, *args, **kw, &block)
|
27
|
+
@expectations << [sym, args, kw, block]
|
28
28
|
self
|
29
29
|
end
|
30
30
|
|
@@ -33,8 +33,8 @@ class FlexMock
|
|
33
33
|
# call).
|
34
34
|
def apply(mock)
|
35
35
|
obj = mock
|
36
|
-
@expectations.each do |sym, args, block|
|
37
|
-
obj = obj.send(sym, *args, &block)
|
36
|
+
@expectations.each do |sym, args, kw, block|
|
37
|
+
obj = obj.send(sym, *args, **kw, &block)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -31,9 +31,9 @@ class FlexMock
|
|
31
31
|
|
32
32
|
WHITELIST = [:with_signature_matching]
|
33
33
|
|
34
|
-
def method_missing(sym, *args, &block)
|
34
|
+
def method_missing(sym, *args, **kw, &block)
|
35
35
|
if explicit? || WHITELIST.include?(sym)
|
36
|
-
@expectation.send(sym, *args, &block)
|
36
|
+
@expectation.send(sym, *args, **kw, &block)
|
37
37
|
else
|
38
38
|
fail NoMethodError, "Cannot stub methods not defined by the base class\n" +
|
39
39
|
" Method: #{@method_name}\n" +
|
@@ -150,8 +150,8 @@ class FlexMock
|
|
150
150
|
# to the result.
|
151
151
|
#
|
152
152
|
# See Expectation for a list of declarators that can be used.
|
153
|
-
def should_receive(*args)
|
154
|
-
flexmock_define_expectation(caller, *args)
|
153
|
+
def should_receive(*args, **kw)
|
154
|
+
flexmock_define_expectation(caller, *args, **kw)
|
155
155
|
end
|
156
156
|
|
157
157
|
def should_expect(*args)
|
@@ -161,13 +161,22 @@ class FlexMock
|
|
161
161
|
# Invoke the original of a mocked method
|
162
162
|
#
|
163
163
|
# Usually called in a #and_return statement
|
164
|
-
def invoke_original(m, *args, &block)
|
165
|
-
|
164
|
+
def invoke_original(m, *args, **kw, &block)
|
165
|
+
if block
|
166
|
+
args << block
|
167
|
+
end
|
168
|
+
flexmock_invoke_original(m, args, kw)
|
166
169
|
end
|
167
170
|
|
168
171
|
# Whether the given method's original definition has been stored
|
169
172
|
def find_original_method(m)
|
170
|
-
it =
|
173
|
+
it =
|
174
|
+
if m.respond_to?(:to_str) || m.respond_to?(:to_sym)
|
175
|
+
@obj.method(m)
|
176
|
+
else
|
177
|
+
m
|
178
|
+
end
|
179
|
+
|
171
180
|
while it && (it.owner != @proxy_definition_module)
|
172
181
|
it = it.super_method
|
173
182
|
end
|
@@ -200,15 +209,24 @@ class FlexMock
|
|
200
209
|
@proxy_definition_module.method_defined?(m)
|
201
210
|
end
|
202
211
|
|
203
|
-
def
|
204
|
-
|
212
|
+
def flexmock_plain_new_method?(m)
|
213
|
+
m.name == :new && m.owner == Class
|
214
|
+
end
|
215
|
+
|
216
|
+
def flexmock_define_expectation(location, *args, **kw)
|
217
|
+
EXP_BUILDER.parse_should_args(self, args, kw) do |method_name|
|
205
218
|
if !has_proxied_method?(method_name)
|
206
219
|
define_proxy_method(method_name)
|
207
220
|
end
|
208
221
|
ex = @mock.flexmock_define_expectation(location, method_name)
|
209
222
|
if FlexMock.partials_verify_signatures
|
210
223
|
if (existing_method = find_original_method(method_name))
|
211
|
-
|
224
|
+
if flexmock_plain_new_method?(existing_method)
|
225
|
+
# Look for the signature of `initialize` instead
|
226
|
+
ex.with_signature_matching(@obj.instance_method(:initialize))
|
227
|
+
else
|
228
|
+
ex.with_signature_matching(existing_method)
|
229
|
+
end
|
212
230
|
end
|
213
231
|
end
|
214
232
|
ex.mock = self
|
@@ -216,17 +234,17 @@ class FlexMock
|
|
216
234
|
end
|
217
235
|
end
|
218
236
|
|
219
|
-
def flexmock_find_expectation(*args)
|
220
|
-
@mock.flexmock_find_expectation(*args)
|
237
|
+
def flexmock_find_expectation(*args, **kw, &block)
|
238
|
+
@mock.flexmock_find_expectation(*args, **kw, &block)
|
221
239
|
end
|
222
240
|
|
223
241
|
def add_mock_method(method_name)
|
224
242
|
proxy_module_eval do
|
225
|
-
define_method(method_name) { |*args, &block|
|
243
|
+
define_method(method_name) { |*args, **kw, &block|
|
226
244
|
proxy = __flexmock_proxy or
|
227
245
|
fail "Missing FlexMock proxy " +
|
228
246
|
"(for method_name=#{method_name.inspect}, self=\#{self})"
|
229
|
-
proxy.send(method_name, *args, &block)
|
247
|
+
proxy.send(method_name, *args, **kw, &block)
|
230
248
|
}
|
231
249
|
end
|
232
250
|
end
|
@@ -262,9 +280,9 @@ class FlexMock
|
|
262
280
|
end
|
263
281
|
|
264
282
|
allocators.each do |allocate_method|
|
265
|
-
flexmock_define_expectation(location, allocate_method).and_return { |*args|
|
283
|
+
flexmock_define_expectation(location, allocate_method).and_return { |*args, **kw|
|
266
284
|
create_new_mocked_object(
|
267
|
-
allocate_method, args, expectation_recorder, block)
|
285
|
+
allocate_method, args, kw, expectation_recorder, block)
|
268
286
|
}
|
269
287
|
end
|
270
288
|
expectation_recorder
|
@@ -276,7 +294,7 @@ class FlexMock
|
|
276
294
|
expectation_blocks = @initialize_expectation_blocks = Array.new
|
277
295
|
expectation_recorders = @initialize_expectation_recorders = Array.new
|
278
296
|
@initialize_override = Module.new do
|
279
|
-
define_method :initialize do |*args, &block|
|
297
|
+
define_method :initialize do |*args, **kw, &block|
|
280
298
|
if self.class.respond_to?(:__flexmock_proxy) && (mock = self.class.__flexmock_proxy)
|
281
299
|
container = mock.flexmock_container
|
282
300
|
mock = container.flexmock(self)
|
@@ -287,7 +305,7 @@ class FlexMock
|
|
287
305
|
r.apply(mock)
|
288
306
|
end
|
289
307
|
end
|
290
|
-
super(*args, &block)
|
308
|
+
super(*args, **kw, &block)
|
291
309
|
end
|
292
310
|
end
|
293
311
|
override = @initialize_override
|
@@ -317,8 +335,8 @@ class FlexMock
|
|
317
335
|
# (2) Pass to the block for custom configuration.
|
318
336
|
# (3) Apply any recorded expecations
|
319
337
|
#
|
320
|
-
def create_new_mocked_object(allocate_method, args, recorder, block)
|
321
|
-
new_obj = flexmock_invoke_original(allocate_method, args,
|
338
|
+
def create_new_mocked_object(allocate_method, args, kw, recorder, block)
|
339
|
+
new_obj = flexmock_invoke_original(allocate_method, args, kw)
|
322
340
|
mock = flexmock_container.flexmock(new_obj)
|
323
341
|
block.call(mock) unless block.nil?
|
324
342
|
recorder.apply(mock)
|
@@ -328,11 +346,15 @@ class FlexMock
|
|
328
346
|
|
329
347
|
# Invoke the original definition of method on the object supported by
|
330
348
|
# the stub.
|
331
|
-
def flexmock_invoke_original(method, args,
|
349
|
+
def flexmock_invoke_original(method, args, kw)
|
332
350
|
if (original_method = find_original_method(method))
|
333
|
-
|
351
|
+
if Proc === args.last
|
352
|
+
block = args.last
|
353
|
+
args = args[0..-2]
|
354
|
+
end
|
355
|
+
original_method.call(*args, **kw, &block)
|
334
356
|
else
|
335
|
-
@obj.__send__(:method_missing, method, *args, &block)
|
357
|
+
@obj.__send__(:method_missing, method, *args, **kw, &block)
|
336
358
|
end
|
337
359
|
end
|
338
360
|
|
@@ -366,8 +388,8 @@ class FlexMock
|
|
366
388
|
end
|
367
389
|
|
368
390
|
# Forward to the mock
|
369
|
-
def flexmock_received?(*args)
|
370
|
-
@mock.flexmock_received?(*args)
|
391
|
+
def flexmock_received?(*args, **kw)
|
392
|
+
@mock.flexmock_received?(*args, **kw)
|
371
393
|
end
|
372
394
|
|
373
395
|
# Forward to the mock
|
@@ -399,15 +421,15 @@ class FlexMock
|
|
399
421
|
|
400
422
|
# Evaluate a block (or string) in the context of the singleton
|
401
423
|
# class of the target partial object.
|
402
|
-
def target_class_eval(*args, &block)
|
403
|
-
target_singleton_class.class_eval(*args, &block)
|
424
|
+
def target_class_eval(*args, **kw, &block)
|
425
|
+
target_singleton_class.class_eval(*args, **kw, &block)
|
404
426
|
end
|
405
427
|
|
406
428
|
class ProxyDefinitionModule < Module
|
407
429
|
end
|
408
430
|
|
409
431
|
# Evaluate a block into the module we use to define the proxy methods
|
410
|
-
def proxy_module_eval(*args, &block)
|
432
|
+
def proxy_module_eval(*args, **kw, &block)
|
411
433
|
if !@proxy_definition_module
|
412
434
|
obj = @obj
|
413
435
|
@proxy_definition_module = m = ProxyDefinitionModule.new do
|
@@ -419,7 +441,7 @@ class FlexMock
|
|
419
441
|
end
|
420
442
|
target_class_eval { prepend m }
|
421
443
|
end
|
422
|
-
@proxy_definition_module.class_eval(*args, &block)
|
444
|
+
@proxy_definition_module.class_eval(*args, **kw, &block)
|
423
445
|
end
|
424
446
|
|
425
447
|
# Hide the existing method definition with a singleton defintion
|
@@ -438,15 +460,15 @@ class FlexMock
|
|
438
460
|
def define_proxy_method(method_name)
|
439
461
|
if method_name =~ /=$/
|
440
462
|
proxy_module_eval do
|
441
|
-
define_method(method_name) do |*args, &block|
|
442
|
-
__flexmock_proxy.mock.__send__(method_name, *args, &block)
|
463
|
+
define_method(method_name) do |*args, **kw, &block|
|
464
|
+
__flexmock_proxy.mock.__send__(method_name, *args, **kw, &block)
|
443
465
|
end
|
444
466
|
end
|
445
467
|
else
|
446
468
|
proxy_module_eval <<-EOD
|
447
|
-
def #{method_name}(*args, &block)
|
469
|
+
def #{method_name}(*args, **kw, &block)
|
448
470
|
FlexMock.verify_mocking_allowed!
|
449
|
-
__flexmock_proxy.mock.#{method_name}(*args, &block)
|
471
|
+
__flexmock_proxy.mock.#{method_name}(*args, **kw, &block)
|
450
472
|
end
|
451
473
|
EOD
|
452
474
|
end
|
@@ -1,25 +1,25 @@
|
|
1
1
|
class FlexMock
|
2
2
|
|
3
3
|
module SpyDescribers
|
4
|
-
def spy_description(spy, sym, args, options)
|
4
|
+
def spy_description(spy, sym, args, kw, options)
|
5
5
|
result = "have received "
|
6
|
-
result << FlexMock.format_call(sym, args)
|
6
|
+
result << FlexMock.format_call(sym, args, kw)
|
7
7
|
result << times_description(options[:times])
|
8
8
|
result << block_description(options[:with_block])
|
9
9
|
result
|
10
10
|
end
|
11
11
|
|
12
|
-
def describe_spy_expectation(spy, sym, args, options={})
|
13
|
-
describe_spy(spy, sym, args, options)
|
12
|
+
def describe_spy_expectation(spy, sym, args, kw, options={})
|
13
|
+
describe_spy(spy, sym, args, kw, options)
|
14
14
|
end
|
15
15
|
|
16
|
-
def describe_spy_negative_expectation(spy, sym, args, options={})
|
17
|
-
describe_spy(spy, sym, args, options, " NOT")
|
16
|
+
def describe_spy_negative_expectation(spy, sym, args, kw, options={})
|
17
|
+
describe_spy(spy, sym, args, kw, options, " NOT")
|
18
18
|
end
|
19
19
|
|
20
|
-
def describe_spy(spy, sym, args, options, not_clause="")
|
20
|
+
def describe_spy(spy, sym, args, kw, options, not_clause="")
|
21
21
|
result = "expected "
|
22
|
-
result << FlexMock.format_call(sym, args)
|
22
|
+
result << FlexMock.format_call(sym, args, kw)
|
23
23
|
result << " to#{not_clause} be received by " << spy.inspect
|
24
24
|
result << times_description(options[:times])
|
25
25
|
result << block_description(options[:with_block])
|
@@ -44,7 +44,7 @@ class FlexMock
|
|
44
44
|
def append_call_record(result, call_record)
|
45
45
|
result <<
|
46
46
|
" " <<
|
47
|
-
FlexMock.format_call(call_record.method_name, call_record.args)
|
47
|
+
FlexMock.format_call(call_record.method_name, call_record.args, call_record.kw)
|
48
48
|
if call_record.expectation
|
49
49
|
result <<
|
50
50
|
" matched by " <<
|
@@ -4,29 +4,36 @@ class FlexMock
|
|
4
4
|
module TestUnitAssertions
|
5
5
|
include FlexMock::SpyDescribers
|
6
6
|
|
7
|
-
def assert_spy_called(spy, method_name, *args)
|
8
|
-
_assert_spy_called(false, spy, method_name, *args)
|
7
|
+
def assert_spy_called(spy, method_name, *args, **kw)
|
8
|
+
_assert_spy_called(false, spy, method_name, *args, **kw)
|
9
9
|
end
|
10
10
|
|
11
|
-
def assert_spy_not_called(spy, method_name, *args)
|
12
|
-
_assert_spy_called(true, spy, method_name, *args)
|
11
|
+
def assert_spy_not_called(spy, method_name, *args, **kw)
|
12
|
+
_assert_spy_called(true, spy, method_name, *args, **kw)
|
13
13
|
end
|
14
14
|
|
15
15
|
private
|
16
16
|
|
17
|
-
def _assert_spy_called(negative, spy, method_name, *args)
|
17
|
+
def _assert_spy_called(negative, spy, method_name, *args, **kw)
|
18
18
|
options = {}
|
19
19
|
if method_name.is_a?(Hash)
|
20
20
|
options = method_name
|
21
21
|
method_name = args.shift
|
22
22
|
end
|
23
|
+
|
24
|
+
# Prior to ruby3, kw args would be matched in *args
|
25
|
+
# thus, expecting any args (:_) implied also expecting
|
26
|
+
# any kw args.
|
27
|
+
kw = :_ if args == [:_]
|
28
|
+
|
23
29
|
args = nil if args == [:_]
|
24
|
-
|
30
|
+
kw = nil if kw == :_
|
31
|
+
bool = spy.flexmock_received?(method_name, args, kw, options)
|
25
32
|
if negative
|
26
33
|
bool = !bool
|
27
|
-
message = describe_spy_negative_expectation(spy, method_name, args, options)
|
34
|
+
message = describe_spy_negative_expectation(spy, method_name, args, kw, options)
|
28
35
|
else
|
29
|
-
message = describe_spy_expectation(spy, method_name, args, options)
|
36
|
+
message = describe_spy_expectation(spy, method_name, args, kw, options)
|
30
37
|
end
|
31
38
|
assert bool, message
|
32
39
|
end
|
data/lib/flexmock/validators.rb
CHANGED
@@ -212,74 +212,28 @@ class FlexMock
|
|
212
212
|
#
|
213
213
|
# @param [Array] args
|
214
214
|
# @raise ValidationFailed
|
215
|
-
def validate(args)
|
216
|
-
|
217
|
-
kw_args = Hash.new
|
218
|
-
|
219
|
-
last_is_proc = false
|
220
|
-
begin
|
221
|
-
if args.last.kind_of?(Proc)
|
222
|
-
args.pop
|
223
|
-
last_is_proc = true
|
224
|
-
end
|
225
|
-
rescue NoMethodError
|
226
|
-
end
|
227
|
-
|
228
|
-
last_is_kw_hash = false
|
229
|
-
if expects_keyword_arguments?
|
230
|
-
last_is_kw_hash =
|
231
|
-
begin
|
232
|
-
args.last.kind_of?(Hash)
|
233
|
-
rescue NoMethodError
|
234
|
-
end
|
215
|
+
def validate(args, kw, block)
|
216
|
+
kw ||= Hash.new
|
235
217
|
|
236
|
-
|
237
|
-
|
238
|
-
elsif requires_keyword_arguments?
|
239
|
-
raise ValidationFailed, "#{@exp} expects keyword arguments but none were provided"
|
240
|
-
end
|
218
|
+
if expects_keyword_arguments? && requires_keyword_arguments? && kw.empty?
|
219
|
+
raise ValidationFailed, "#{@exp} expects keyword arguments but none were provided"
|
241
220
|
end
|
242
221
|
|
243
|
-
|
244
|
-
|
245
|
-
positional_count = args.size
|
246
|
-
|
247
|
-
if required_arguments > positional_count
|
248
|
-
if requires_keyword_arguments?
|
249
|
-
raise ValidationFailed, "#{@exp} expects at least #{required_arguments} positional arguments but got only #{positional_count}"
|
250
|
-
end
|
251
|
-
|
252
|
-
if (required_arguments - positional_count) == 1 && (last_is_kw_hash || last_is_proc)
|
253
|
-
if last_is_kw_hash
|
254
|
-
last_is_kw_hash = false
|
255
|
-
kw_args = Hash.new
|
256
|
-
else
|
257
|
-
last_is_proc = false
|
258
|
-
end
|
259
|
-
positional_count += 1
|
260
|
-
elsif (required_arguments - positional_count) == 2 && (last_is_kw_hash && last_is_proc)
|
261
|
-
last_is_kw_hash = false
|
262
|
-
kw_args = Hash.new
|
263
|
-
last_is_proc = false
|
264
|
-
positional_count += 2
|
265
|
-
else
|
266
|
-
raise ValidationFailed, "#{@exp} expects at least #{required_arguments} positional arguments but got only #{positional_count}"
|
267
|
-
end
|
222
|
+
if required_arguments > args.size
|
223
|
+
raise ValidationFailed, "#{@exp} expects at least #{required_arguments} positional arguments but got only #{args.size}"
|
268
224
|
end
|
269
225
|
|
270
|
-
if !splat? && (required_arguments + optional_arguments) <
|
271
|
-
|
272
|
-
raise ValidationFailed, "#{@exp} expects at most #{required_arguments + optional_arguments} positional arguments but got #{positional_count}"
|
273
|
-
end
|
226
|
+
if !splat? && (required_arguments + optional_arguments) < args.size
|
227
|
+
raise ValidationFailed, "#{@exp} expects at most #{required_arguments + optional_arguments} positional arguments but got #{args.size}"
|
274
228
|
end
|
275
229
|
|
276
230
|
missing_keyword_arguments = required_keyword_arguments.
|
277
|
-
find_all { |k| !
|
231
|
+
find_all { |k| !kw.has_key?(k) }
|
278
232
|
if !missing_keyword_arguments.empty?
|
279
233
|
raise ValidationFailed, "#{@exp} missing required keyword arguments #{missing_keyword_arguments.map(&:to_s).sort.join(", ")}"
|
280
234
|
end
|
281
235
|
if !keyword_splat?
|
282
|
-
|
236
|
+
kw.each_key do |k|
|
283
237
|
if !optional_keyword_arguments.include?(k) && !required_keyword_arguments.include?(k)
|
284
238
|
raise ValidationFailed, "#{@exp} given unexpected keyword argument #{k}"
|
285
239
|
end
|
@@ -313,4 +267,3 @@ class FlexMock
|
|
313
267
|
end
|
314
268
|
end
|
315
269
|
end
|
316
|
-
|
data/lib/flexmock/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flexmock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jim Weirich
|
8
8
|
- Sylvain Joyeux
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2024-
|
12
|
+
date: 2024-08-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -157,7 +157,7 @@ homepage: https://github.com/doudou/flexmock
|
|
157
157
|
licenses:
|
158
158
|
- MIT
|
159
159
|
metadata: {}
|
160
|
-
post_install_message:
|
160
|
+
post_install_message:
|
161
161
|
rdoc_options: []
|
162
162
|
require_paths:
|
163
163
|
- lib
|
@@ -165,15 +165,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
165
165
|
requirements:
|
166
166
|
- - ">="
|
167
167
|
- !ruby/object:Gem::Version
|
168
|
-
version: '
|
168
|
+
version: '3.0'
|
169
169
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
170
170
|
requirements:
|
171
171
|
- - ">="
|
172
172
|
- !ruby/object:Gem::Version
|
173
173
|
version: '0'
|
174
174
|
requirements: []
|
175
|
-
rubygems_version: 3.
|
176
|
-
signing_key:
|
175
|
+
rubygems_version: 3.4.20
|
176
|
+
signing_key:
|
177
177
|
specification_version: 4
|
178
178
|
summary: Simple and Flexible Mock Objects for Testing
|
179
179
|
test_files: []
|