flexmock 2.4.5 → 3.0.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/.github/workflows/test.yml +5 -1
- data/README.md +60 -6
- data/flexmock.gemspec +1 -1
- data/lib/flexmock/argument_matchers.rb +3 -48
- 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 +5 -6
- data/lib/flexmock/core.rb +21 -18
- data/lib/flexmock/core_class_methods.rb +28 -21
- data/lib/flexmock/expectation.rb +68 -93
- 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 -32
- 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,44 @@ 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
|
+
|
25
63
|
2.4.0:
|
26
64
|
- forward-compatible implementation of `with_kw_args`, `with_any_kw_args`,
|
27
65
|
`with_block` and `with_no_block`. The objective of this release is to ensure
|
@@ -556,12 +594,10 @@ determining whether a given expectation matches or not.
|
|
556
594
|
series. The last value will be repeatably returned if the number of
|
557
595
|
matching calls exceeds the number of values.
|
558
596
|
|
559
|
-
* <b>and_return { |<em
|
597
|
+
* <b>and_return { |<em>*args</em>, <em>**kw</em>, <em>&block</em>| <em>code</em> ... }</b>
|
560
598
|
|
561
599
|
Declares that the expected message will return the yielded value of
|
562
600
|
the block. The block will receive all the arguments in the message.
|
563
|
-
If the message was provided a block, it will be passed as the last
|
564
|
-
parameter of the block's argument list.
|
565
601
|
|
566
602
|
* <b>returns( ... )</b>
|
567
603
|
|
@@ -765,13 +801,15 @@ The following rules are used for argument matching:
|
|
765
801
|
|
766
802
|
will match any even integer.
|
767
803
|
|
768
|
-
*
|
769
|
-
|
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`
|
770
808
|
|
771
809
|
Example:
|
772
810
|
|
773
811
|
```ruby
|
774
|
-
m.should_receive(:foo).with(Integer
|
812
|
+
m.should_receive(:foo).with(Integer).with_block.and_return(:got_block)
|
775
813
|
m.should_receive(:foo).with(Integer).and_return(:no_block)
|
776
814
|
```
|
777
815
|
|
@@ -782,6 +820,22 @@ The following rules are used for argument matching:
|
|
782
820
|
m.foo(1) => returns :no_block
|
783
821
|
```
|
784
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
|
+
|
785
839
|
### Creating Partial Mocks
|
786
840
|
|
787
841
|
Sometimes it is useful to mock the behavior of one or two methods in
|
data/flexmock.gemspec
CHANGED
@@ -62,41 +62,11 @@ class FlexMock
|
|
62
62
|
def ===(target)
|
63
63
|
@hash.all? { |k, v| target[k] == v }
|
64
64
|
end
|
65
|
-
def
|
66
|
-
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
####################################################################
|
71
|
-
# Match hashes that match all the fields of +hash+.
|
72
|
-
class KwArgsMatcher
|
73
|
-
def initialize(expected)
|
74
|
-
@expected = expected
|
75
|
-
end
|
76
|
-
def ===(target)
|
77
|
-
return false unless target.kind_of?(Hash)
|
78
|
-
matching = @expected.all? do |k, v|
|
79
|
-
v === target[k] || v == target[k]
|
80
|
-
end
|
81
|
-
return false unless matching
|
82
|
-
|
83
|
-
@expected.size == target.size
|
65
|
+
def empty?
|
66
|
+
@hash.empty?
|
84
67
|
end
|
85
68
|
def inspect
|
86
|
-
|
87
|
-
k_s = case k
|
88
|
-
when Symbol
|
89
|
-
"#{k}: "
|
90
|
-
else
|
91
|
-
"#{k.inspect} => "
|
92
|
-
end
|
93
|
-
|
94
|
-
v_s = FlexMock.forbid_mocking("<recursive call to mocked method in #inspect>") do
|
95
|
-
v.inspect
|
96
|
-
end
|
97
|
-
"#{k_s}#{v_s}"
|
98
|
-
end
|
99
|
-
args.join(", ")
|
69
|
+
"hsh(#{@hash.inspect})"
|
100
70
|
end
|
101
71
|
end
|
102
72
|
|
@@ -113,19 +83,4 @@ class FlexMock
|
|
113
83
|
"ducktype(#{@methods.map{|m| m.inspect}.join(',')})"
|
114
84
|
end
|
115
85
|
end
|
116
|
-
|
117
|
-
####################################################################
|
118
|
-
# Match objects that implement all the methods in +methods+.
|
119
|
-
class OptionalProcMatcher
|
120
|
-
def initialize
|
121
|
-
end
|
122
|
-
def ===(target)
|
123
|
-
ArgumentMatching.missing?(target) || Proc === target
|
124
|
-
end
|
125
|
-
def inspect
|
126
|
-
"optional_proc"
|
127
|
-
end
|
128
|
-
end
|
129
|
-
OPTIONAL_PROC_MATCHER = OptionalProcMatcher.new
|
130
|
-
|
131
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
|
@@ -3,8 +3,7 @@ class FlexMock
|
|
3
3
|
# A composite expectation allows several expectations to be grouped into a
|
4
4
|
# single composite and then apply the same constraints to all expectations
|
5
5
|
# in the group.
|
6
|
-
class CompositeExpectation
|
7
|
-
attr_reader :expectations
|
6
|
+
class CompositeExpectation
|
8
7
|
|
9
8
|
# Initialize the composite expectation.
|
10
9
|
def initialize
|
@@ -17,9 +16,9 @@ class FlexMock
|
|
17
16
|
end
|
18
17
|
|
19
18
|
# Apply the constraint method to all expectations in the composite.
|
20
|
-
def method_missing(sym, *args, &block)
|
19
|
+
def method_missing(sym, *args, **kw, &block)
|
21
20
|
@expectations.each do |expectation|
|
22
|
-
expectation.send(sym, *args, &block)
|
21
|
+
expectation.send(sym, *args, **kw, &block)
|
23
22
|
end
|
24
23
|
self
|
25
24
|
end
|
@@ -39,9 +38,9 @@ class FlexMock
|
|
39
38
|
|
40
39
|
# Start a new method expectation. The following constraints will be
|
41
40
|
# applied to the new expectation.
|
42
|
-
def should_receive(*args, &block)
|
41
|
+
def should_receive(*args, **kw, &block)
|
43
42
|
@expectations.first.mock.
|
44
|
-
flexmock_define_expectation(caller, *args, &block)
|
43
|
+
flexmock_define_expectation(caller, *args, **kw, &block)
|
45
44
|
end
|
46
45
|
|
47
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,35 +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
|
-
args.join(', ')
|
95
|
-
else
|
96
|
-
"*args"
|
97
|
-
end
|
98
|
-
end
|
99
99
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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"
|
106
114
|
end
|
107
|
-
|
108
|
-
|
109
|
-
end
|
115
|
+
|
116
|
+
[(args unless args.empty?), kw].compact.join(", ")
|
110
117
|
end
|
111
118
|
|
112
119
|
# Check will assert the block returns true. If it doesn't, an
|