rspec-mocks 3.1.3 → 3.2.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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +5 -3
- data/Changelog.md +65 -2
- data/README.md +25 -5
- data/lib/rspec/mocks/any_instance/chain.rb +1 -0
- data/lib/rspec/mocks/any_instance/stub_chain.rb +2 -1
- data/lib/rspec/mocks/argument_list_matcher.rb +36 -8
- data/lib/rspec/mocks/argument_matchers.rb +63 -16
- data/lib/rspec/mocks/configuration.rb +35 -0
- data/lib/rspec/mocks/error_generator.rb +69 -17
- data/lib/rspec/mocks/example_methods.rb +59 -6
- data/lib/rspec/mocks/matchers/have_received.rb +4 -6
- data/lib/rspec/mocks/matchers/receive.rb +25 -1
- data/lib/rspec/mocks/matchers/receive_message_chain.rb +14 -0
- data/lib/rspec/mocks/matchers/receive_messages.rb +4 -0
- data/lib/rspec/mocks/message_expectation.rb +308 -277
- data/lib/rspec/mocks/method_double.rb +25 -2
- data/lib/rspec/mocks/mutate_const.rb +19 -8
- data/lib/rspec/mocks/object_reference.rb +81 -23
- data/lib/rspec/mocks/proxy.rb +42 -4
- data/lib/rspec/mocks/standalone.rb +1 -1
- data/lib/rspec/mocks/verifying_double.rb +17 -10
- data/lib/rspec/mocks/verifying_message_expecation.rb +9 -17
- data/lib/rspec/mocks/verifying_proxy.rb +4 -2
- data/lib/rspec/mocks/version.rb +1 -1
- metadata +57 -25
- metadata.gz.sig +0 -0
@@ -10,6 +10,16 @@ module RSpec
|
|
10
10
|
# Raised when doubles or partial doubles are used outside of the per-test lifecycle.
|
11
11
|
OutsideOfExampleError = Class.new(StandardError)
|
12
12
|
|
13
|
+
# Raised when an expectation customization method (e.g. `with`,
|
14
|
+
# `and_return`) is called on a message expectation which has already been
|
15
|
+
# invoked.
|
16
|
+
MockExpectationAlreadyInvokedError = Class.new(Exception)
|
17
|
+
|
18
|
+
# Raised for situations that RSpec cannot support due to mutations made
|
19
|
+
# externally on arguments that RSpec is holding onto to use for later
|
20
|
+
# comparisons.
|
21
|
+
CannotSupportArgMutationsError = Class.new(StandardError)
|
22
|
+
|
13
23
|
# @private
|
14
24
|
UnsupportedMatcherError = Class.new(StandardError)
|
15
25
|
# @private
|
@@ -40,28 +50,42 @@ module RSpec
|
|
40
50
|
def raise_unexpected_message_args_error(expectation, *args)
|
41
51
|
expected_args = format_args(*expectation.expected_args)
|
42
52
|
actual_args = format_received_args(*args)
|
43
|
-
|
44
|
-
|
45
|
-
|
53
|
+
diff = diff_message(expectation.expected_args, args)
|
54
|
+
|
55
|
+
message = default_error_message(expectation, expected_args, actual_args)
|
56
|
+
message << "\nDiff:#{diff}" unless diff.empty?
|
57
|
+
|
58
|
+
__raise message
|
46
59
|
end
|
47
60
|
|
48
61
|
# @private
|
49
62
|
def raise_missing_default_stub_error(expectation, *args)
|
50
63
|
expected_args = format_args(*expectation.expected_args)
|
51
64
|
actual_args = format_received_args(*args)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
65
|
+
diff = diff_message(expectation.expected_args, args)
|
66
|
+
|
67
|
+
message = default_error_message(expectation, expected_args, actual_args)
|
68
|
+
message << "\nDiff:\n #{diff}" unless diff.empty?
|
69
|
+
message << "\n Please stub a default value first if message might be received with other args as well. \n"
|
70
|
+
|
71
|
+
__raise message
|
56
72
|
end
|
57
73
|
|
58
74
|
# @private
|
59
75
|
def raise_similar_message_args_error(expectation, *args_for_multiple_calls)
|
60
76
|
expected_args = format_args(*expectation.expected_args)
|
61
77
|
actual_args = args_for_multiple_calls.map { |a| format_received_args(*a) }.join(", ")
|
62
|
-
|
63
|
-
|
64
|
-
|
78
|
+
|
79
|
+
__raise(default_error_message(expectation, expected_args, actual_args))
|
80
|
+
end
|
81
|
+
|
82
|
+
def default_error_message(expectation, expected_args, actual_args)
|
83
|
+
[
|
84
|
+
intro,
|
85
|
+
"received",
|
86
|
+
expectation.message.inspect,
|
87
|
+
unexpected_arguments_message(expected_args, actual_args),
|
88
|
+
].join(" ")
|
65
89
|
end
|
66
90
|
|
67
91
|
# rubocop:disable Style/ParameterLists
|
@@ -143,8 +167,8 @@ module RSpec
|
|
143
167
|
end
|
144
168
|
|
145
169
|
# @private
|
146
|
-
def describe_expectation(message, expected_received_count, _actual_received_count, *args)
|
147
|
-
"
|
170
|
+
def describe_expectation(verb, message, expected_received_count, _actual_received_count, *args)
|
171
|
+
"#{verb} #{message}#{format_args(*args)} #{count_message(expected_received_count)}"
|
148
172
|
end
|
149
173
|
|
150
174
|
# @private
|
@@ -194,6 +218,36 @@ module RSpec
|
|
194
218
|
|
195
219
|
private
|
196
220
|
|
221
|
+
def unexpected_arguments_message(expected_args_string, actual_args_string)
|
222
|
+
"with unexpected arguments\n expected: #{expected_args_string}\n got: #{actual_args_string}"
|
223
|
+
end
|
224
|
+
|
225
|
+
def diff_message(expected_args, actual_args)
|
226
|
+
formatted_expected_args = expected_args.map do |x|
|
227
|
+
RSpec::Support.rspec_description_for_object(x)
|
228
|
+
end
|
229
|
+
|
230
|
+
formatted_expected_args, actual_args = unpack_string_args(formatted_expected_args, actual_args)
|
231
|
+
|
232
|
+
differ.diff(actual_args, formatted_expected_args)
|
233
|
+
end
|
234
|
+
|
235
|
+
def unpack_string_args(formatted_expected_args, actual_args)
|
236
|
+
if [formatted_expected_args, actual_args].all? { |x| list_of_exactly_one_string?(x) }
|
237
|
+
[formatted_expected_args.first, actual_args.first]
|
238
|
+
else
|
239
|
+
[formatted_expected_args, actual_args]
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def list_of_exactly_one_string?(args)
|
244
|
+
Array === args && args.count == 1 && String === args.first
|
245
|
+
end
|
246
|
+
|
247
|
+
def differ
|
248
|
+
RSpec::Support::Differ.new(:color => RSpec::Mocks.configuration.color?)
|
249
|
+
end
|
250
|
+
|
197
251
|
def intro
|
198
252
|
if @name
|
199
253
|
"Double #{@name.inspect}"
|
@@ -222,13 +276,11 @@ module RSpec
|
|
222
276
|
end
|
223
277
|
|
224
278
|
def arg_list(*args)
|
225
|
-
args.map { |arg| arg_has_valid_description(arg) ? arg.description : arg.inspect }.join(", ")
|
279
|
+
args.map { |arg| arg_has_valid_description?(arg) ? arg.description : arg.inspect }.join(", ")
|
226
280
|
end
|
227
281
|
|
228
|
-
def arg_has_valid_description(arg)
|
229
|
-
|
230
|
-
|
231
|
-
!arg.description.nil? && !arg.description.empty?
|
282
|
+
def arg_has_valid_description?(arg)
|
283
|
+
RSpec::Support.is_a_matcher?(arg) && arg.respond_to?(:description)
|
232
284
|
end
|
233
285
|
|
234
286
|
def format_received_args(*args)
|
@@ -11,11 +11,11 @@ module RSpec
|
|
11
11
|
|
12
12
|
# @overload double()
|
13
13
|
# @overload double(name)
|
14
|
-
# @param name [String/Symbol]
|
14
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
15
15
|
# @overload double(stubs)
|
16
16
|
# @param stubs (Hash) hash of message/return-value pairs
|
17
17
|
# @overload double(name, stubs)
|
18
|
-
# @param name [String/Symbol]
|
18
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
19
19
|
# @param stubs (Hash) hash of message/return-value pairs
|
20
20
|
# @return (Double)
|
21
21
|
#
|
@@ -38,9 +38,16 @@ module RSpec
|
|
38
38
|
|
39
39
|
# @overload instance_double(doubled_class)
|
40
40
|
# @param doubled_class [String, Class]
|
41
|
+
# @overload instance_double(doubled_class, name)
|
42
|
+
# @param doubled_class [String, Class]
|
43
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
41
44
|
# @overload instance_double(doubled_class, stubs)
|
42
45
|
# @param doubled_class [String, Class]
|
43
46
|
# @param stubs [Hash] hash of message/return-value pairs
|
47
|
+
# @overload instance_double(doubled_class, name, stubs)
|
48
|
+
# @param doubled_class [String, Class]
|
49
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
50
|
+
# @param stubs [Hash] hash of message/return-value pairs
|
44
51
|
# @return InstanceVerifyingDouble
|
45
52
|
#
|
46
53
|
# Constructs a test double against a specific class. If the given class
|
@@ -54,9 +61,16 @@ module RSpec
|
|
54
61
|
|
55
62
|
# @overload class_double(doubled_class)
|
56
63
|
# @param doubled_class [String, Module]
|
64
|
+
# @overload class_double(doubled_class, name)
|
65
|
+
# @param doubled_class [String, Module]
|
66
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
57
67
|
# @overload class_double(doubled_class, stubs)
|
58
68
|
# @param doubled_class [String, Module]
|
59
69
|
# @param stubs [Hash] hash of message/return-value pairs
|
70
|
+
# @overload class_double(doubled_class, name, stubs)
|
71
|
+
# @param doubled_class [String, Module]
|
72
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
73
|
+
# @param stubs [Hash] hash of message/return-value pairs
|
60
74
|
# @return ClassVerifyingDouble
|
61
75
|
#
|
62
76
|
# Constructs a test double against a specific class. If the given class
|
@@ -70,9 +84,16 @@ module RSpec
|
|
70
84
|
|
71
85
|
# @overload object_double(object_or_name)
|
72
86
|
# @param object_or_name [String, Object]
|
87
|
+
# @overload object_double(object_or_name, name)
|
88
|
+
# @param object_or_name [String, Object]
|
89
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
73
90
|
# @overload object_double(object_or_name, stubs)
|
74
91
|
# @param object_or_name [String, Object]
|
75
92
|
# @param stubs [Hash] hash of message/return-value pairs
|
93
|
+
# @overload object_double(object_or_name, name, stubs)
|
94
|
+
# @param object_or_name [String, Object]
|
95
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
96
|
+
# @param stubs [Hash] hash of message/return-value pairs
|
76
97
|
# @return ObjectVerifyingDouble
|
77
98
|
#
|
78
99
|
# Constructs a test double against a specific object. Only the methods
|
@@ -86,11 +107,11 @@ module RSpec
|
|
86
107
|
|
87
108
|
# @overload spy()
|
88
109
|
# @overload spy(name)
|
89
|
-
# @param name [String/Symbol]
|
110
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
90
111
|
# @overload spy(stubs)
|
91
112
|
# @param stubs (Hash) hash of message/return-value pairs
|
92
113
|
# @overload spy(name, stubs)
|
93
|
-
# @param name [String/Symbol]
|
114
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
94
115
|
# @param stubs (Hash) hash of message/return-value pairs
|
95
116
|
# @return (Double)
|
96
117
|
#
|
@@ -103,9 +124,16 @@ module RSpec
|
|
103
124
|
|
104
125
|
# @overload instance_spy(doubled_class)
|
105
126
|
# @param doubled_class [String, Class]
|
127
|
+
# @overload instance_spy(doubled_class, name)
|
128
|
+
# @param doubled_class [String, Class]
|
129
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
106
130
|
# @overload instance_spy(doubled_class, stubs)
|
107
131
|
# @param doubled_class [String, Class]
|
108
132
|
# @param stubs [Hash] hash of message/return-value pairs
|
133
|
+
# @overload instance_spy(doubled_class, name, stubs)
|
134
|
+
# @param doubled_class [String, Class]
|
135
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
136
|
+
# @param stubs [Hash] hash of message/return-value pairs
|
109
137
|
# @return InstanceVerifyingDouble
|
110
138
|
#
|
111
139
|
# Constructs a test double that is optimized for use with `have_received`
|
@@ -120,9 +148,16 @@ module RSpec
|
|
120
148
|
|
121
149
|
# @overload object_spy(object_or_name)
|
122
150
|
# @param object_or_name [String, Object]
|
151
|
+
# @overload object_spy(object_or_name, name)
|
152
|
+
# @param object_or_name [String, Class]
|
153
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
123
154
|
# @overload object_spy(object_or_name, stubs)
|
124
155
|
# @param object_or_name [String, Object]
|
125
156
|
# @param stubs [Hash] hash of message/return-value pairs
|
157
|
+
# @overload object_spy(object_or_name, name, stubs)
|
158
|
+
# @param object_or_name [String, Class]
|
159
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
160
|
+
# @param stubs [Hash] hash of message/return-value pairs
|
126
161
|
# @return ObjectVerifyingDouble
|
127
162
|
#
|
128
163
|
# Constructs a test double that is optimized for use with `have_received`
|
@@ -136,9 +171,16 @@ module RSpec
|
|
136
171
|
|
137
172
|
# @overload class_spy(doubled_class)
|
138
173
|
# @param doubled_class [String, Module]
|
174
|
+
# @overload class_spy(doubled_class, name)
|
175
|
+
# @param doubled_class [String, Class]
|
176
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
139
177
|
# @overload class_spy(doubled_class, stubs)
|
140
178
|
# @param doubled_class [String, Module]
|
141
179
|
# @param stubs [Hash] hash of message/return-value pairs
|
180
|
+
# @overload class_spy(doubled_class, name, stubs)
|
181
|
+
# @param doubled_class [String, Class]
|
182
|
+
# @param name [String/Symbol] name or description to be used in failure messages
|
183
|
+
# @param stubs [Hash] hash of message/return-value pairs
|
142
184
|
# @return ClassVerifyingDouble
|
143
185
|
#
|
144
186
|
# Constructs a test double that is optimized for use with `have_received`
|
@@ -350,15 +392,26 @@ module RSpec
|
|
350
392
|
end
|
351
393
|
end
|
352
394
|
|
395
|
+
# @private
|
396
|
+
def self.extended(object)
|
397
|
+
# This gets extended in so that if `RSpec::Matchers` is included in
|
398
|
+
# `klass` later, it's definition of `expect` will take precedence.
|
399
|
+
object.extend ExpectHost unless object.respond_to?(:expect)
|
400
|
+
end
|
401
|
+
|
353
402
|
# @private
|
354
403
|
def self.declare_verifying_double(type, ref, *args)
|
355
404
|
if RSpec::Mocks.configuration.verify_doubled_constant_names? &&
|
356
405
|
!ref.defined?
|
357
406
|
|
358
407
|
raise VerifyingDoubleNotDefinedError,
|
359
|
-
"#{ref.description} is not a defined constant. " \
|
408
|
+
"#{ref.description.inspect} is not a defined constant. " \
|
360
409
|
"Perhaps you misspelt it? " \
|
361
|
-
"Disable check with verify_doubled_constant_names configuration option."
|
410
|
+
"Disable check with `verify_doubled_constant_names` configuration option."
|
411
|
+
end
|
412
|
+
|
413
|
+
RSpec::Mocks.configuration.verifying_double_declaration_callbacks.each do |block|
|
414
|
+
block.call(ref)
|
362
415
|
end
|
363
416
|
|
364
417
|
declare_double(type, ref, *args)
|
@@ -44,7 +44,7 @@ module RSpec
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def description
|
47
|
-
expect.
|
47
|
+
(@expectation ||= expect).description_for("have received")
|
48
48
|
end
|
49
49
|
|
50
50
|
CONSTRAINTS.each do |expectation|
|
@@ -75,11 +75,9 @@ module RSpec
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def expect
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
expectation
|
82
|
-
end
|
78
|
+
expectation = mock_proxy.build_expectation(@method_name)
|
79
|
+
apply_constraints_to expectation
|
80
|
+
expectation
|
83
81
|
end
|
84
82
|
|
85
83
|
def apply_constraints_to(expectation)
|
@@ -15,9 +15,13 @@ module RSpec
|
|
15
15
|
"receive"
|
16
16
|
end
|
17
17
|
|
18
|
+
def description
|
19
|
+
describable.description_for("receive")
|
20
|
+
end
|
21
|
+
|
18
22
|
def setup_expectation(subject, &block)
|
19
23
|
warn_if_any_instance("expect", subject)
|
20
|
-
setup_mock_proxy_method_substitute(subject, :add_message_expectation, block)
|
24
|
+
@describable = setup_mock_proxy_method_substitute(subject, :add_message_expectation, block)
|
21
25
|
end
|
22
26
|
alias matches? setup_expectation
|
23
27
|
|
@@ -60,6 +64,10 @@ module RSpec
|
|
60
64
|
|
61
65
|
private
|
62
66
|
|
67
|
+
def describable
|
68
|
+
@describable ||= DefaultDescribable.new(@message)
|
69
|
+
end
|
70
|
+
|
63
71
|
def warn_if_any_instance(expression, subject)
|
64
72
|
return unless AnyInstance::Proxy === subject
|
65
73
|
|
@@ -100,6 +108,22 @@ module RSpec
|
|
100
108
|
last.block ||= block
|
101
109
|
nil
|
102
110
|
end
|
111
|
+
|
112
|
+
# MessageExpectation objects are able to describe themselves in detail.
|
113
|
+
# We use this as a fall back when a MessageExpectation is not available.
|
114
|
+
# @private
|
115
|
+
class DefaultDescribable
|
116
|
+
def initialize(message)
|
117
|
+
@message = message
|
118
|
+
end
|
119
|
+
|
120
|
+
# This is much simpler for the `any_instance` case than what the
|
121
|
+
# user may want, but I'm not up for putting a bunch of effort
|
122
|
+
# into full descriptions for `any_instance` expectations at this point :(.
|
123
|
+
def description_for(verb)
|
124
|
+
"#{verb} #{@message}"
|
125
|
+
end
|
126
|
+
end
|
103
127
|
end
|
104
128
|
end
|
105
129
|
end
|
@@ -22,6 +22,10 @@ module RSpec
|
|
22
22
|
"receive_message_chain"
|
23
23
|
end
|
24
24
|
|
25
|
+
def description
|
26
|
+
"receive message chain #{formatted_chain}"
|
27
|
+
end
|
28
|
+
|
25
29
|
def setup_allowance(subject, &block)
|
26
30
|
chain = StubChain.stub_chain_on(subject, *@chain, &(@block || block))
|
27
31
|
replay_customizations(chain)
|
@@ -60,6 +64,16 @@ module RSpec
|
|
60
64
|
customization.playback_onto(chain)
|
61
65
|
end
|
62
66
|
end
|
67
|
+
|
68
|
+
def formatted_chain
|
69
|
+
@formatted_chain ||= @chain.map do |part|
|
70
|
+
if Hash === part
|
71
|
+
part.keys.first.to_s
|
72
|
+
else
|
73
|
+
part.to_s
|
74
|
+
end
|
75
|
+
end.join(".")
|
76
|
+
end
|
63
77
|
end
|
64
78
|
end
|
65
79
|
end
|
@@ -12,6 +12,10 @@ module RSpec
|
|
12
12
|
"receive_messages"
|
13
13
|
end
|
14
14
|
|
15
|
+
def description
|
16
|
+
"receive messages: #{@message_return_value_hash.inspect}"
|
17
|
+
end
|
18
|
+
|
15
19
|
def setup_expectation(subject)
|
16
20
|
warn_about_block if block_given?
|
17
21
|
each_message_on(proxy_on(subject)) do |host, message, return_value|
|
@@ -34,47 +34,11 @@ module RSpec
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
-
#
|
37
|
+
# Represents an individual method stub or message expectation. The methods
|
38
|
+
# defined here can be used to configure how it behaves. The methods return
|
39
|
+
# `self` so that they can be chained together to form a fluent interface.
|
38
40
|
class MessageExpectation
|
39
|
-
#
|
40
|
-
attr_accessor :error_generator, :implementation
|
41
|
-
attr_reader :message
|
42
|
-
attr_reader :orig_object
|
43
|
-
attr_writer :expected_received_count, :expected_from, :argument_list_matcher
|
44
|
-
protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation=
|
45
|
-
|
46
|
-
# rubocop:disable Style/ParameterLists
|
47
|
-
# @private
|
48
|
-
def initialize(error_generator, expectation_ordering, expected_from, method_double,
|
49
|
-
type=:expectation, opts={}, &implementation_block)
|
50
|
-
@error_generator = error_generator
|
51
|
-
@error_generator.opts = opts
|
52
|
-
@expected_from = expected_from
|
53
|
-
@method_double = method_double
|
54
|
-
@orig_object = @method_double.object
|
55
|
-
@message = @method_double.method_name
|
56
|
-
@actual_received_count = 0
|
57
|
-
@expected_received_count = type == :expectation ? 1 : :any
|
58
|
-
@argument_list_matcher = ArgumentListMatcher::MATCH_ALL
|
59
|
-
@order_group = expectation_ordering
|
60
|
-
@order_group.register(self) unless type == :stub
|
61
|
-
@expectation_type = type
|
62
|
-
@ordered = false
|
63
|
-
@at_least = @at_most = @exactly = nil
|
64
|
-
@args_to_yield = []
|
65
|
-
@failed_fast = nil
|
66
|
-
@eval_context = nil
|
67
|
-
@yield_receiver_to_implementation_block = false
|
68
|
-
|
69
|
-
@implementation = Implementation.new
|
70
|
-
self.inner_implementation_action = implementation_block
|
71
|
-
end
|
72
|
-
# rubocop:enable Style/ParameterLists
|
73
|
-
|
74
|
-
# @private
|
75
|
-
def expected_args
|
76
|
-
@argument_list_matcher.expected_args
|
77
|
-
end
|
41
|
+
# @!group Configuring Responses
|
78
42
|
|
79
43
|
# @overload and_return(value)
|
80
44
|
# @overload and_return(first_value, second_value)
|
@@ -87,8 +51,8 @@ module RSpec
|
|
87
51
|
# If the message is received more times than there are values, the last
|
88
52
|
# value is received for every subsequent call.
|
89
53
|
#
|
54
|
+
# @return [nil] No further chaining is supported after this.
|
90
55
|
# @example
|
91
|
-
#
|
92
56
|
# allow(counter).to receive(:count).and_return(1)
|
93
57
|
# counter.count # => 1
|
94
58
|
# counter.count # => 1
|
@@ -101,6 +65,7 @@ module RSpec
|
|
101
65
|
# counter.count # => 3
|
102
66
|
# # etc
|
103
67
|
def and_return(first_value, *values)
|
68
|
+
raise_already_invoked_error_if_necessary(__method__)
|
104
69
|
if negative?
|
105
70
|
raise "`and_return` is not supported with negative message expectations"
|
106
71
|
end
|
@@ -116,22 +81,13 @@ module RSpec
|
|
116
81
|
nil
|
117
82
|
end
|
118
83
|
|
119
|
-
def and_yield_receiver_to_implementation
|
120
|
-
@yield_receiver_to_implementation_block = true
|
121
|
-
self
|
122
|
-
end
|
123
|
-
|
124
|
-
def yield_receiver_to_implementation_block?
|
125
|
-
@yield_receiver_to_implementation_block
|
126
|
-
end
|
127
|
-
|
128
84
|
# Tells the object to delegate to the original unmodified method
|
129
85
|
# when it receives the message.
|
130
86
|
#
|
131
87
|
# @note This is only available on partial doubles.
|
132
88
|
#
|
89
|
+
# @return [nil] No further chaining is supported after this.
|
133
90
|
# @example
|
134
|
-
#
|
135
91
|
# expect(counter).to receive(:increment).and_call_original
|
136
92
|
# original_count = counter.count
|
137
93
|
# counter.increment
|
@@ -149,12 +105,11 @@ module RSpec
|
|
149
105
|
#
|
150
106
|
# @note This is only available on partial doubles.
|
151
107
|
#
|
108
|
+
# @return [nil] No further chaining is supported after this.
|
152
109
|
# @example
|
153
|
-
#
|
154
110
|
# expect(api).to receive(:large_list).and_wrap_original do |original_method, *args, &block|
|
155
111
|
# original_method.call(*args, &block).first(10)
|
156
112
|
# end
|
157
|
-
#
|
158
113
|
def and_wrap_original(&block)
|
159
114
|
if RSpec::Mocks::TestDouble === @method_double.object
|
160
115
|
@error_generator.raise_only_valid_on_a_partial_double(:and_call_original)
|
@@ -163,6 +118,8 @@ module RSpec
|
|
163
118
|
@implementation = AndWrapOriginalImplementation.new(@method_double.original_method, block)
|
164
119
|
@yield_receiver_to_implementation_block = false
|
165
120
|
end
|
121
|
+
|
122
|
+
nil
|
166
123
|
end
|
167
124
|
|
168
125
|
# @overload and_raise
|
@@ -172,8 +129,8 @@ module RSpec
|
|
172
129
|
#
|
173
130
|
# Tells the object to raise an exception when the message is received.
|
174
131
|
#
|
132
|
+
# @return [nil] No further chaining is supported after this.
|
175
133
|
# @note
|
176
|
-
#
|
177
134
|
# When you pass an exception class, the MessageExpectation will raise
|
178
135
|
# an instance of it, creating it with `exception` and passing `message`
|
179
136
|
# if specified. If the exception class initializer requires more than
|
@@ -181,12 +138,12 @@ module RSpec
|
|
181
138
|
# otherwise this method will raise an ArgumentError exception.
|
182
139
|
#
|
183
140
|
# @example
|
184
|
-
#
|
185
141
|
# allow(car).to receive(:go).and_raise
|
186
142
|
# allow(car).to receive(:go).and_raise(OutOfGas)
|
187
143
|
# allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive")
|
188
144
|
# allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz))
|
189
145
|
def and_raise(exception=RuntimeError, message=nil)
|
146
|
+
raise_already_invoked_error_if_necessary(__method__)
|
190
147
|
if exception.respond_to?(:exception)
|
191
148
|
exception = message ? exception.exception(message) : exception.exception
|
192
149
|
end
|
@@ -201,11 +158,12 @@ module RSpec
|
|
201
158
|
# Tells the object to throw a symbol (with the object if that form is
|
202
159
|
# used) when the message is received.
|
203
160
|
#
|
161
|
+
# @return [nil] No further chaining is supported after this.
|
204
162
|
# @example
|
205
|
-
#
|
206
163
|
# allow(car).to receive(:go).and_throw(:out_of_gas)
|
207
164
|
# allow(car).to receive(:go).and_throw(:out_of_gas, :level => 0.1)
|
208
165
|
def and_throw(*args)
|
166
|
+
raise_already_invoked_error_if_necessary(__method__)
|
209
167
|
self.terminal_implementation_action = Proc.new { throw(*args) }
|
210
168
|
nil
|
211
169
|
end
|
@@ -213,167 +171,28 @@ module RSpec
|
|
213
171
|
# Tells the object to yield one or more args to a block when the message
|
214
172
|
# is received.
|
215
173
|
#
|
174
|
+
# @return [MessageExpecation] self, to support further chaining.
|
216
175
|
# @example
|
217
|
-
#
|
218
176
|
# stream.stub(:open).and_yield(StringIO.new)
|
219
177
|
def and_yield(*args, &block)
|
178
|
+
raise_already_invoked_error_if_necessary(__method__)
|
220
179
|
yield @eval_context = Object.new if block
|
221
180
|
@args_to_yield << args
|
222
181
|
self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
|
223
182
|
self
|
224
183
|
end
|
184
|
+
# @!endgroup
|
225
185
|
|
226
|
-
#
|
227
|
-
def matches?(message, *args)
|
228
|
-
@message == message && @argument_list_matcher.args_match?(*args)
|
229
|
-
end
|
230
|
-
|
231
|
-
# @private
|
232
|
-
def safe_invoke(parent_stub, *args, &block)
|
233
|
-
invoke_incrementing_actual_calls_by(1, false, parent_stub, *args, &block)
|
234
|
-
end
|
235
|
-
|
236
|
-
# @private
|
237
|
-
def invoke(parent_stub, *args, &block)
|
238
|
-
invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block)
|
239
|
-
end
|
240
|
-
|
241
|
-
# @private
|
242
|
-
def invoke_without_incrementing_received_count(parent_stub, *args, &block)
|
243
|
-
invoke_incrementing_actual_calls_by(0, true, parent_stub, *args, &block)
|
244
|
-
end
|
245
|
-
|
246
|
-
# @private
|
247
|
-
def negative?
|
248
|
-
@expected_received_count == 0 && !@at_least
|
249
|
-
end
|
250
|
-
|
251
|
-
# @private
|
252
|
-
def called_max_times?
|
253
|
-
@expected_received_count != :any &&
|
254
|
-
!@at_least &&
|
255
|
-
@expected_received_count > 0 &&
|
256
|
-
@actual_received_count >= @expected_received_count
|
257
|
-
end
|
258
|
-
|
259
|
-
# @private
|
260
|
-
def matches_name_but_not_args(message, *args)
|
261
|
-
@message == message && !@argument_list_matcher.args_match?(*args)
|
262
|
-
end
|
263
|
-
|
264
|
-
# @private
|
265
|
-
def verify_messages_received
|
266
|
-
InsertOntoBacktrace.line(@expected_from) do
|
267
|
-
generate_error unless expected_messages_received? || failed_fast?
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
# @private
|
272
|
-
def expected_messages_received?
|
273
|
-
ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
|
274
|
-
end
|
275
|
-
|
276
|
-
def ensure_expected_ordering_received!
|
277
|
-
@order_group.verify_invocation_order(self) if @ordered
|
278
|
-
true
|
279
|
-
end
|
280
|
-
|
281
|
-
# @private
|
282
|
-
def ignoring_args?
|
283
|
-
@expected_received_count == :any
|
284
|
-
end
|
285
|
-
|
286
|
-
# @private
|
287
|
-
def matches_at_least_count?
|
288
|
-
@at_least && @actual_received_count >= @expected_received_count
|
289
|
-
end
|
290
|
-
|
291
|
-
# @private
|
292
|
-
def matches_at_most_count?
|
293
|
-
@at_most && @actual_received_count <= @expected_received_count
|
294
|
-
end
|
295
|
-
|
296
|
-
# @private
|
297
|
-
def matches_exact_count?
|
298
|
-
@expected_received_count == @actual_received_count
|
299
|
-
end
|
300
|
-
|
301
|
-
# @private
|
302
|
-
def similar_messages
|
303
|
-
@similar_messages ||= []
|
304
|
-
end
|
305
|
-
|
306
|
-
# @private
|
307
|
-
def advise(*args)
|
308
|
-
similar_messages << args
|
309
|
-
end
|
310
|
-
|
311
|
-
# @private
|
312
|
-
def generate_error
|
313
|
-
if similar_messages.empty?
|
314
|
-
@error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args)
|
315
|
-
else
|
316
|
-
@error_generator.raise_similar_message_args_error(self, *@similar_messages)
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|
320
|
-
def expectation_count_type
|
321
|
-
return :at_least if @at_least
|
322
|
-
return :at_most if @at_most
|
323
|
-
nil
|
324
|
-
end
|
325
|
-
|
326
|
-
# @private
|
327
|
-
def description
|
328
|
-
@error_generator.describe_expectation(@message, @expected_received_count, @actual_received_count, *expected_args)
|
329
|
-
end
|
330
|
-
|
331
|
-
def raise_out_of_order_error
|
332
|
-
@error_generator.raise_out_of_order_error @message
|
333
|
-
end
|
334
|
-
|
335
|
-
# Constrains a stub or message expectation to invocations with specific
|
336
|
-
# arguments.
|
337
|
-
#
|
338
|
-
# With a stub, if the message might be received with other args as well,
|
339
|
-
# you should stub a default value first, and then stub or mock the same
|
340
|
-
# message using `with` to constrain to specific arguments.
|
341
|
-
#
|
342
|
-
# A message expectation will fail if the message is received with different
|
343
|
-
# arguments.
|
344
|
-
#
|
345
|
-
# @example
|
346
|
-
#
|
347
|
-
# allow(cart).to receive(:add) { :failure }
|
348
|
-
# allow(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
|
349
|
-
# cart.add(Book.new(:isbn => 1234567890))
|
350
|
-
# # => :failure
|
351
|
-
# cart.add(Book.new(:isbn => 1934356379))
|
352
|
-
# # => :success
|
353
|
-
#
|
354
|
-
# expect(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
|
355
|
-
# cart.add(Book.new(:isbn => 1234567890))
|
356
|
-
# # => failed expectation
|
357
|
-
# cart.add(Book.new(:isbn => 1934356379))
|
358
|
-
# # => passes
|
359
|
-
def with(*args, &block)
|
360
|
-
if args.empty?
|
361
|
-
raise ArgumentError,
|
362
|
-
"`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
|
363
|
-
end
|
364
|
-
|
365
|
-
self.inner_implementation_action = block
|
366
|
-
@argument_list_matcher = ArgumentListMatcher.new(*args)
|
367
|
-
self
|
368
|
-
end
|
186
|
+
# @!group Constraining Receive Counts
|
369
187
|
|
370
188
|
# Constrain a message expectation to be received a specific number of
|
371
189
|
# times.
|
372
190
|
#
|
191
|
+
# @return [MessageExpecation] self, to support further chaining.
|
373
192
|
# @example
|
374
|
-
#
|
375
193
|
# expect(dealer).to receive(:deal_card).exactly(10).times
|
376
194
|
def exactly(n, &block)
|
195
|
+
raise_already_invoked_error_if_necessary(__method__)
|
377
196
|
self.inner_implementation_action = block
|
378
197
|
set_expected_received_count :exactly, n
|
379
198
|
self
|
@@ -382,10 +201,11 @@ module RSpec
|
|
382
201
|
# Constrain a message expectation to be received at least a specific
|
383
202
|
# number of times.
|
384
203
|
#
|
204
|
+
# @return [MessageExpecation] self, to support further chaining.
|
385
205
|
# @example
|
386
|
-
#
|
387
206
|
# expect(dealer).to receive(:deal_card).at_least(9).times
|
388
207
|
def at_least(n, &block)
|
208
|
+
raise_already_invoked_error_if_necessary(__method__)
|
389
209
|
set_expected_received_count :at_least, n
|
390
210
|
|
391
211
|
if n == 0
|
@@ -400,10 +220,11 @@ module RSpec
|
|
400
220
|
# Constrain a message expectation to be received at most a specific
|
401
221
|
# number of times.
|
402
222
|
#
|
223
|
+
# @return [MessageExpecation] self, to support further chaining.
|
403
224
|
# @example
|
404
|
-
#
|
405
225
|
# expect(dealer).to receive(:deal_card).at_most(10).times
|
406
226
|
def at_most(n, &block)
|
227
|
+
raise_already_invoked_error_if_necessary(__method__)
|
407
228
|
self.inner_implementation_action = block
|
408
229
|
set_expected_received_count :at_most, n
|
409
230
|
self
|
@@ -411,8 +232,8 @@ module RSpec
|
|
411
232
|
|
412
233
|
# Syntactic sugar for `exactly`, `at_least` and `at_most`
|
413
234
|
#
|
235
|
+
# @return [MessageExpecation] self, to support further chaining.
|
414
236
|
# @example
|
415
|
-
#
|
416
237
|
# expect(dealer).to receive(:deal_card).exactly(10).times
|
417
238
|
# expect(dealer).to receive(:deal_card).at_least(10).times
|
418
239
|
# expect(dealer).to receive(:deal_card).at_most(10).times
|
@@ -423,8 +244,8 @@ module RSpec
|
|
423
244
|
|
424
245
|
# Expect a message not to be received at all.
|
425
246
|
#
|
247
|
+
# @return [MessageExpecation] self, to support further chaining.
|
426
248
|
# @example
|
427
|
-
#
|
428
249
|
# expect(car).to receive(:stop).never
|
429
250
|
def never
|
430
251
|
ErrorGenerator.raise_double_negation_error("expect(obj)") if negative?
|
@@ -434,8 +255,8 @@ module RSpec
|
|
434
255
|
|
435
256
|
# Expect a message to be received exactly one time.
|
436
257
|
#
|
258
|
+
# @return [MessageExpecation] self, to support further chaining.
|
437
259
|
# @example
|
438
|
-
#
|
439
260
|
# expect(car).to receive(:go).once
|
440
261
|
def once(&block)
|
441
262
|
self.inner_implementation_action = block
|
@@ -445,8 +266,8 @@ module RSpec
|
|
445
266
|
|
446
267
|
# Expect a message to be received exactly two times.
|
447
268
|
#
|
269
|
+
# @return [MessageExpecation] self, to support further chaining.
|
448
270
|
# @example
|
449
|
-
#
|
450
271
|
# expect(car).to receive(:go).twice
|
451
272
|
def twice(&block)
|
452
273
|
self.inner_implementation_action = block
|
@@ -456,19 +277,58 @@ module RSpec
|
|
456
277
|
|
457
278
|
# Expect a message to be received exactly three times.
|
458
279
|
#
|
280
|
+
# @return [MessageExpecation] self, to support further chaining.
|
459
281
|
# @example
|
460
|
-
#
|
461
282
|
# expect(car).to receive(:go).thrice
|
462
283
|
def thrice(&block)
|
463
284
|
self.inner_implementation_action = block
|
464
285
|
set_expected_received_count :exactly, 3
|
465
286
|
self
|
466
287
|
end
|
288
|
+
# @!endgroup
|
467
289
|
|
468
|
-
#
|
290
|
+
# @!group Other Constraints
|
291
|
+
|
292
|
+
# Constrains a stub or message expectation to invocations with specific
|
293
|
+
# arguments.
|
294
|
+
#
|
295
|
+
# With a stub, if the message might be received with other args as well,
|
296
|
+
# you should stub a default value first, and then stub or mock the same
|
297
|
+
# message using `with` to constrain to specific arguments.
|
298
|
+
#
|
299
|
+
# A message expectation will fail if the message is received with different
|
300
|
+
# arguments.
|
469
301
|
#
|
302
|
+
# @return [MessageExpecation] self, to support further chaining.
|
470
303
|
# @example
|
304
|
+
# allow(cart).to receive(:add) { :failure }
|
305
|
+
# allow(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
|
306
|
+
# cart.add(Book.new(:isbn => 1234567890))
|
307
|
+
# # => :failure
|
308
|
+
# cart.add(Book.new(:isbn => 1934356379))
|
309
|
+
# # => :success
|
310
|
+
#
|
311
|
+
# expect(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
|
312
|
+
# cart.add(Book.new(:isbn => 1234567890))
|
313
|
+
# # => failed expectation
|
314
|
+
# cart.add(Book.new(:isbn => 1934356379))
|
315
|
+
# # => passes
|
316
|
+
def with(*args, &block)
|
317
|
+
raise_already_invoked_error_if_necessary(__method__)
|
318
|
+
if args.empty?
|
319
|
+
raise ArgumentError,
|
320
|
+
"`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
|
321
|
+
end
|
322
|
+
|
323
|
+
self.inner_implementation_action = block
|
324
|
+
@argument_list_matcher = ArgumentListMatcher.new(*args)
|
325
|
+
self
|
326
|
+
end
|
327
|
+
|
328
|
+
# Expect messages to be received in a specific order.
|
471
329
|
#
|
330
|
+
# @return [MessageExpecation] self, to support further chaining.
|
331
|
+
# @example
|
472
332
|
# expect(api).to receive(:prepare).ordered
|
473
333
|
# expect(api).to receive(:run).ordered
|
474
334
|
# expect(api).to receive(:finish).ordered
|
@@ -482,93 +342,264 @@ module RSpec
|
|
482
342
|
end
|
483
343
|
|
484
344
|
# @private
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
345
|
+
# Contains the parts of `MessageExpecation` that aren't part of
|
346
|
+
# rspec-mocks' public API. The class is very big and could really use
|
347
|
+
# some collaborators it delegates to for this stuff but for now this was
|
348
|
+
# the simplest way to split the public from private stuff to make it
|
349
|
+
# easier to publish the docs for the APIs we want published.
|
350
|
+
module ImplementationDetails
|
351
|
+
attr_accessor :error_generator, :implementation
|
352
|
+
attr_reader :message
|
353
|
+
attr_reader :orig_object
|
354
|
+
attr_writer :expected_received_count, :expected_from, :argument_list_matcher
|
355
|
+
protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation=
|
356
|
+
|
357
|
+
# rubocop:disable Style/ParameterLists
|
358
|
+
def initialize(error_generator, expectation_ordering, expected_from, method_double,
|
359
|
+
type=:expectation, opts={}, &implementation_block)
|
360
|
+
@error_generator = error_generator
|
361
|
+
@error_generator.opts = opts
|
362
|
+
@expected_from = expected_from
|
363
|
+
@method_double = method_double
|
364
|
+
@orig_object = @method_double.object
|
365
|
+
@message = @method_double.method_name
|
366
|
+
@actual_received_count = 0
|
367
|
+
@expected_received_count = type == :expectation ? 1 : :any
|
368
|
+
@argument_list_matcher = ArgumentListMatcher::MATCH_ALL
|
369
|
+
@order_group = expectation_ordering
|
370
|
+
@order_group.register(self) unless type == :stub
|
371
|
+
@expectation_type = type
|
372
|
+
@ordered = false
|
373
|
+
@at_least = @at_most = @exactly = nil
|
374
|
+
@args_to_yield = []
|
375
|
+
@failed_fast = nil
|
376
|
+
@eval_context = nil
|
377
|
+
@yield_receiver_to_implementation_block = false
|
489
378
|
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
379
|
+
@implementation = Implementation.new
|
380
|
+
self.inner_implementation_action = implementation_block
|
381
|
+
end
|
382
|
+
# rubocop:enable Style/ParameterLists
|
494
383
|
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
end
|
384
|
+
def expected_args
|
385
|
+
@argument_list_matcher.expected_args
|
386
|
+
end
|
499
387
|
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
388
|
+
def and_yield_receiver_to_implementation
|
389
|
+
@yield_receiver_to_implementation_block = true
|
390
|
+
self
|
391
|
+
end
|
504
392
|
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
end
|
393
|
+
def yield_receiver_to_implementation_block?
|
394
|
+
@yield_receiver_to_implementation_block
|
395
|
+
end
|
509
396
|
|
510
|
-
|
397
|
+
def matches?(message, *args)
|
398
|
+
@message == message && @argument_list_matcher.args_match?(*args)
|
399
|
+
end
|
400
|
+
|
401
|
+
def safe_invoke(parent_stub, *args, &block)
|
402
|
+
invoke_incrementing_actual_calls_by(1, false, parent_stub, *args, &block)
|
403
|
+
end
|
404
|
+
|
405
|
+
def invoke(parent_stub, *args, &block)
|
406
|
+
invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block)
|
407
|
+
end
|
408
|
+
|
409
|
+
def invoke_without_incrementing_received_count(parent_stub, *args, &block)
|
410
|
+
invoke_incrementing_actual_calls_by(0, true, parent_stub, *args, &block)
|
411
|
+
end
|
511
412
|
|
512
|
-
|
513
|
-
|
413
|
+
def negative?
|
414
|
+
@expected_received_count == 0 && !@at_least
|
415
|
+
end
|
514
416
|
|
515
|
-
|
516
|
-
@
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
@error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args)
|
417
|
+
def called_max_times?
|
418
|
+
@expected_received_count != :any &&
|
419
|
+
!@at_least &&
|
420
|
+
@expected_received_count > 0 &&
|
421
|
+
@actual_received_count >= @expected_received_count
|
521
422
|
end
|
522
423
|
|
523
|
-
|
424
|
+
def matches_name_but_not_args(message, *args)
|
425
|
+
@message == message && !@argument_list_matcher.args_match?(*args)
|
426
|
+
end
|
524
427
|
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
elsif parent_stub
|
529
|
-
parent_stub.invoke(nil, *args, &block)
|
428
|
+
def verify_messages_received
|
429
|
+
InsertOntoBacktrace.line(@expected_from) do
|
430
|
+
generate_error unless expected_messages_received? || failed_fast?
|
530
431
|
end
|
531
|
-
ensure
|
532
|
-
@actual_received_count += increment
|
533
432
|
end
|
534
|
-
end
|
535
433
|
|
536
|
-
|
537
|
-
|
538
|
-
|
434
|
+
def expected_messages_received?
|
435
|
+
ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
|
436
|
+
end
|
539
437
|
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
@expected_received_count = case n
|
545
|
-
when Numeric then n
|
546
|
-
when :once then 1
|
547
|
-
when :twice then 2
|
548
|
-
when :thrice then 3
|
549
|
-
end
|
550
|
-
end
|
438
|
+
def ensure_expected_ordering_received!
|
439
|
+
@order_group.verify_invocation_order(self) if @ordered
|
440
|
+
true
|
441
|
+
end
|
551
442
|
|
552
|
-
|
553
|
-
|
554
|
-
|
443
|
+
def ignoring_args?
|
444
|
+
@expected_received_count == :any
|
445
|
+
end
|
555
446
|
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
implementation.inner_action = action
|
560
|
-
end
|
447
|
+
def matches_at_least_count?
|
448
|
+
@at_least && @actual_received_count >= @expected_received_count
|
449
|
+
end
|
561
450
|
|
562
|
-
|
563
|
-
|
564
|
-
|
451
|
+
def matches_at_most_count?
|
452
|
+
@at_most && @actual_received_count <= @expected_received_count
|
453
|
+
end
|
454
|
+
|
455
|
+
def matches_exact_count?
|
456
|
+
@expected_received_count == @actual_received_count
|
457
|
+
end
|
458
|
+
|
459
|
+
def similar_messages
|
460
|
+
@similar_messages ||= []
|
461
|
+
end
|
462
|
+
|
463
|
+
def advise(*args)
|
464
|
+
similar_messages << args
|
465
|
+
end
|
466
|
+
|
467
|
+
def generate_error
|
468
|
+
if similar_messages.empty?
|
469
|
+
@error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args)
|
470
|
+
else
|
471
|
+
@error_generator.raise_similar_message_args_error(self, *@similar_messages)
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
def expectation_count_type
|
476
|
+
return :at_least if @at_least
|
477
|
+
return :at_most if @at_most
|
478
|
+
nil
|
479
|
+
end
|
480
|
+
|
481
|
+
def description_for(verb)
|
482
|
+
@error_generator.describe_expectation(
|
483
|
+
verb, @message, @expected_received_count,
|
484
|
+
@actual_received_count, *expected_args
|
485
|
+
)
|
486
|
+
end
|
487
|
+
|
488
|
+
def raise_out_of_order_error
|
489
|
+
@error_generator.raise_out_of_order_error @message
|
490
|
+
end
|
491
|
+
|
492
|
+
def additional_expected_calls
|
493
|
+
return 0 if @expectation_type == :stub || !@exactly
|
494
|
+
@expected_received_count - 1
|
495
|
+
end
|
496
|
+
|
497
|
+
def ordered?
|
498
|
+
@ordered
|
499
|
+
end
|
500
|
+
|
501
|
+
def negative_expectation_for?(message)
|
502
|
+
@message == message && negative?
|
503
|
+
end
|
504
|
+
|
505
|
+
def actual_received_count_matters?
|
506
|
+
@at_least || @at_most || @exactly
|
507
|
+
end
|
565
508
|
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
)
|
509
|
+
def increase_actual_received_count!
|
510
|
+
@actual_received_count += 1
|
511
|
+
end
|
512
|
+
|
513
|
+
def fail_if_problematic_received_arg_mutations(received_arg_list)
|
514
|
+
return if @argument_list_matcher == ArgumentListMatcher::MATCH_ALL
|
515
|
+
return unless received_arg_list.has_mutations?
|
516
|
+
|
517
|
+
raise CannotSupportArgMutationsError,
|
518
|
+
"`have_received(...).with(...)` cannot be used when received " \
|
519
|
+
"message args have later been mutated. You can use a normal " \
|
520
|
+
"message expectation (`expect(...).to receive(...).with(...)`) " \
|
521
|
+
"instead."
|
522
|
+
end
|
523
|
+
|
524
|
+
private
|
525
|
+
|
526
|
+
def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block)
|
527
|
+
args.unshift(orig_object) if yield_receiver_to_implementation_block?
|
528
|
+
|
529
|
+
if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count))
|
530
|
+
@actual_received_count += increment
|
531
|
+
@failed_fast = true
|
532
|
+
# args are the args we actually received, @argument_list_matcher is the
|
533
|
+
# list of args we were expecting
|
534
|
+
@error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args)
|
535
|
+
end
|
536
|
+
|
537
|
+
@order_group.handle_order_constraint self
|
538
|
+
|
539
|
+
begin
|
540
|
+
if implementation.present?
|
541
|
+
implementation.call(*args, &block)
|
542
|
+
elsif parent_stub
|
543
|
+
parent_stub.invoke(nil, *args, &block)
|
544
|
+
end
|
545
|
+
ensure
|
546
|
+
@actual_received_count += increment
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
def has_been_invoked?
|
551
|
+
@actual_received_count > 0
|
552
|
+
end
|
553
|
+
|
554
|
+
def raise_already_invoked_error_if_necessary(calling_customization)
|
555
|
+
return unless has_been_invoked?
|
556
|
+
|
557
|
+
error_message = "The message expectation for #{orig_object.inspect}.#{message} has already been invoked " \
|
558
|
+
"and cannot be modified further (e.g. using `#{calling_customization}`). All message expectation " \
|
559
|
+
"customizations must be applied before it is used for the first time."
|
560
|
+
|
561
|
+
raise MockExpectationAlreadyInvokedError, error_message
|
562
|
+
end
|
563
|
+
|
564
|
+
def failed_fast?
|
565
|
+
@failed_fast
|
566
|
+
end
|
567
|
+
|
568
|
+
def set_expected_received_count(relativity, n)
|
569
|
+
@at_least = (relativity == :at_least)
|
570
|
+
@at_most = (relativity == :at_most)
|
571
|
+
@exactly = (relativity == :exactly)
|
572
|
+
@expected_received_count = case n
|
573
|
+
when Numeric then n
|
574
|
+
when :once then 1
|
575
|
+
when :twice then 2
|
576
|
+
when :thrice then 3
|
577
|
+
end
|
578
|
+
end
|
579
|
+
|
580
|
+
def initial_implementation_action=(action)
|
581
|
+
implementation.initial_action = action
|
582
|
+
end
|
583
|
+
|
584
|
+
def inner_implementation_action=(action)
|
585
|
+
return unless action
|
586
|
+
warn_about_stub_override if implementation.inner_action
|
587
|
+
implementation.inner_action = action
|
588
|
+
end
|
589
|
+
|
590
|
+
def terminal_implementation_action=(action)
|
591
|
+
implementation.terminal_action = action
|
592
|
+
end
|
593
|
+
|
594
|
+
def warn_about_stub_override
|
595
|
+
RSpec.warning(
|
596
|
+
"You're overriding a previous stub implementation of `#{@message}`. " \
|
597
|
+
"Called from #{CallerFilter.first_non_rspec_line}."
|
598
|
+
)
|
599
|
+
end
|
571
600
|
end
|
601
|
+
|
602
|
+
include ImplementationDetails
|
572
603
|
end
|
573
604
|
|
574
605
|
# Handles the implementation of an `and_yield` declaration.
|