rspec-mocks 3.0.4 → 3.12.6
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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +1 -1
- data/Changelog.md +512 -2
- data/{License.txt → LICENSE.md} +5 -4
- data/README.md +113 -30
- data/lib/rspec/mocks/any_instance/chain.rb +5 -3
- data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
- data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +1 -5
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +9 -8
- data/lib/rspec/mocks/any_instance/message_chains.rb +7 -8
- data/lib/rspec/mocks/any_instance/proxy.rb +14 -5
- data/lib/rspec/mocks/any_instance/recorder.rb +61 -31
- data/lib/rspec/mocks/any_instance/stub_chain.rb +15 -11
- data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +1 -5
- data/lib/rspec/mocks/any_instance.rb +1 -0
- data/lib/rspec/mocks/argument_list_matcher.rb +55 -10
- data/lib/rspec/mocks/argument_matchers.rb +88 -30
- data/lib/rspec/mocks/configuration.rb +61 -13
- data/lib/rspec/mocks/error_generator.rb +250 -107
- data/lib/rspec/mocks/example_methods.rb +151 -28
- data/lib/rspec/mocks/instance_method_stasher.rb +17 -6
- data/lib/rspec/mocks/matchers/have_received.rb +50 -20
- data/lib/rspec/mocks/matchers/receive.rb +39 -11
- data/lib/rspec/mocks/matchers/receive_message_chain.rb +22 -7
- data/lib/rspec/mocks/matchers/receive_messages.rb +12 -7
- data/lib/rspec/mocks/message_chain.rb +3 -7
- data/lib/rspec/mocks/message_expectation.rb +466 -307
- data/lib/rspec/mocks/method_double.rb +88 -29
- data/lib/rspec/mocks/method_reference.rb +85 -25
- data/lib/rspec/mocks/minitest_integration.rb +68 -0
- data/lib/rspec/mocks/mutate_const.rb +50 -109
- data/lib/rspec/mocks/object_reference.rb +89 -32
- data/lib/rspec/mocks/order_group.rb +4 -5
- data/lib/rspec/mocks/proxy.rb +156 -60
- data/lib/rspec/mocks/space.rb +52 -35
- data/lib/rspec/mocks/standalone.rb +1 -1
- data/lib/rspec/mocks/syntax.rb +26 -30
- data/lib/rspec/mocks/targets.rb +55 -28
- data/lib/rspec/mocks/test_double.rb +43 -7
- data/lib/rspec/mocks/verifying_double.rb +27 -33
- data/lib/rspec/mocks/{verifying_message_expecation.rb → verifying_message_expectation.rb} +11 -16
- data/lib/rspec/mocks/verifying_proxy.rb +77 -26
- data/lib/rspec/mocks/version.rb +1 -1
- data/lib/rspec/mocks.rb +8 -1
- data.tar.gz.sig +0 -0
- metadata +80 -43
- metadata.gz.sig +0 -0
@@ -1,15 +1,15 @@
|
|
1
|
+
RSpec::Support.require_rspec_support 'mutex'
|
2
|
+
|
1
3
|
module RSpec
|
2
4
|
module Mocks
|
3
|
-
|
4
5
|
# A message expectation that only allows concrete return values to be set
|
5
6
|
# for a message. While this same effect can be achieved using a standard
|
6
|
-
#
|
7
|
+
# MessageExpectation, this version is much faster and so can be used as an
|
7
8
|
# optimization.
|
8
9
|
#
|
9
10
|
# @private
|
10
11
|
class SimpleMessageExpectation
|
11
|
-
|
12
|
-
def initialize(message, response, error_generator, backtrace_line = nil)
|
12
|
+
def initialize(message, response, error_generator, backtrace_line=nil)
|
13
13
|
@message, @response, @error_generator, @backtrace_line = message.to_sym, response, error_generator, backtrace_line
|
14
14
|
@received = false
|
15
15
|
end
|
@@ -28,53 +28,21 @@ module RSpec
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def verify_messages_received
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
end
|
31
|
+
return if @received
|
32
|
+
@error_generator.raise_expectation_error(
|
33
|
+
@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil, [], @backtrace_line
|
34
|
+
)
|
36
35
|
end
|
37
|
-
end
|
38
36
|
|
39
|
-
|
40
|
-
class MessageExpectation
|
41
|
-
# @private
|
42
|
-
attr_accessor :error_generator, :implementation
|
43
|
-
attr_reader :message
|
44
|
-
attr_reader :orig_object
|
45
|
-
attr_writer :expected_received_count, :expected_from, :argument_list_matcher
|
46
|
-
protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation=
|
47
|
-
|
48
|
-
# @private
|
49
|
-
def initialize(error_generator, expectation_ordering, expected_from, method_double,
|
50
|
-
type=:expectation, opts={}, &implementation_block)
|
51
|
-
@error_generator = error_generator
|
52
|
-
@error_generator.opts = opts
|
53
|
-
@expected_from = expected_from
|
54
|
-
@method_double = method_double
|
55
|
-
@orig_object = @method_double.object
|
56
|
-
@message = @method_double.method_name
|
57
|
-
@actual_received_count = 0
|
58
|
-
@expected_received_count = type == :expectation ? 1 : :any
|
59
|
-
@argument_list_matcher = ArgumentListMatcher::MATCH_ALL
|
60
|
-
@order_group = expectation_ordering
|
61
|
-
@order_group.register(self) unless type == :stub
|
62
|
-
@expectation_type = type
|
63
|
-
@ordered = false
|
64
|
-
@at_least = @at_most = @exactly = nil
|
65
|
-
@args_to_yield = []
|
66
|
-
@failed_fast = nil
|
67
|
-
@eval_context = nil
|
68
|
-
@yield_receiver_to_implementation_block = false
|
69
|
-
|
70
|
-
@implementation = Implementation.new
|
71
|
-
self.inner_implementation_action = implementation_block
|
37
|
+
def unadvise(_)
|
72
38
|
end
|
39
|
+
end
|
73
40
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
41
|
+
# Represents an individual method stub or message expectation. The methods
|
42
|
+
# defined here can be used to configure how it behaves. The methods return
|
43
|
+
# `self` so that they can be chained together to form a fluent interface.
|
44
|
+
class MessageExpectation
|
45
|
+
# @!group Configuring Responses
|
78
46
|
|
79
47
|
# @overload and_return(value)
|
80
48
|
# @overload and_return(first_value, second_value)
|
@@ -85,10 +53,10 @@ module RSpec
|
|
85
53
|
# etc.
|
86
54
|
#
|
87
55
|
# If the message is received more times than there are values, the last
|
88
|
-
# value is
|
56
|
+
# value is returned for every subsequent call.
|
89
57
|
#
|
58
|
+
# @return [nil] No further chaining is supported after this.
|
90
59
|
# @example
|
91
|
-
#
|
92
60
|
# allow(counter).to receive(:count).and_return(1)
|
93
61
|
# counter.count # => 1
|
94
62
|
# counter.count # => 1
|
@@ -101,6 +69,7 @@ module RSpec
|
|
101
69
|
# counter.count # => 3
|
102
70
|
# # etc
|
103
71
|
def and_return(first_value, *values)
|
72
|
+
raise_already_invoked_error_if_necessary(__method__)
|
104
73
|
if negative?
|
105
74
|
raise "`and_return` is not supported with negative message expectations"
|
106
75
|
end
|
@@ -110,19 +79,52 @@ module RSpec
|
|
110
79
|
end
|
111
80
|
|
112
81
|
values.unshift(first_value)
|
113
|
-
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0
|
82
|
+
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least)
|
114
83
|
self.terminal_implementation_action = AndReturnImplementation.new(values)
|
115
84
|
|
116
85
|
nil
|
117
86
|
end
|
118
87
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
88
|
+
# Tells the object to invoke a Proc when it receives the message. Given
|
89
|
+
# more than one value, the result of the first Proc is returned the first
|
90
|
+
# time the message is received, the result of the second Proc is returned
|
91
|
+
# the next time, etc, etc.
|
92
|
+
#
|
93
|
+
# If the message is received more times than there are Procs, the result of
|
94
|
+
# the last Proc is returned for every subsequent call.
|
95
|
+
#
|
96
|
+
# @return [nil] No further chaining is supported after this.
|
97
|
+
# @example
|
98
|
+
# allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout })
|
99
|
+
# api.get_foo # => raises ApiTimeout
|
100
|
+
# api.get_foo # => raises ApiTimeout
|
101
|
+
#
|
102
|
+
# allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout }, -> { raise ApiTimeout }, -> { :a_foo })
|
103
|
+
# api.get_foo # => raises ApiTimeout
|
104
|
+
# api.get_foo # => rasies ApiTimeout
|
105
|
+
# api.get_foo # => :a_foo
|
106
|
+
# api.get_foo # => :a_foo
|
107
|
+
# api.get_foo # => :a_foo
|
108
|
+
# # etc
|
109
|
+
def and_invoke(first_proc, *procs)
|
110
|
+
raise_already_invoked_error_if_necessary(__method__)
|
111
|
+
if negative?
|
112
|
+
raise "`and_invoke` is not supported with negative message expectations"
|
113
|
+
end
|
114
|
+
|
115
|
+
if block_given?
|
116
|
+
raise ArgumentError, "Implementation blocks aren't supported with `and_invoke`"
|
117
|
+
end
|
123
118
|
|
124
|
-
|
125
|
-
|
119
|
+
procs.unshift(first_proc)
|
120
|
+
if procs.any? { |p| !p.respond_to?(:call) }
|
121
|
+
raise ArgumentError, "Arguments to `and_invoke` must be callable."
|
122
|
+
end
|
123
|
+
|
124
|
+
@expected_received_count = [@expected_received_count, procs.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least)
|
125
|
+
self.terminal_implementation_action = AndInvokeImplementation.new(procs)
|
126
|
+
|
127
|
+
nil
|
126
128
|
end
|
127
129
|
|
128
130
|
# Tells the object to delegate to the original unmodified method
|
@@ -130,20 +132,35 @@ module RSpec
|
|
130
132
|
#
|
131
133
|
# @note This is only available on partial doubles.
|
132
134
|
#
|
135
|
+
# @return [nil] No further chaining is supported after this.
|
133
136
|
# @example
|
134
|
-
#
|
135
137
|
# expect(counter).to receive(:increment).and_call_original
|
136
138
|
# original_count = counter.count
|
137
139
|
# counter.increment
|
138
140
|
# expect(counter.count).to eq(original_count + 1)
|
139
141
|
def and_call_original
|
140
|
-
|
141
|
-
|
142
|
-
else
|
143
|
-
warn_about_stub_override if implementation.inner_action
|
144
|
-
@implementation = AndCallOriginalImplementation.new(@method_double.original_method)
|
145
|
-
@yield_receiver_to_implementation_block = false
|
142
|
+
block = lambda do |original, *args, &b|
|
143
|
+
original.call(*args, &b)
|
146
144
|
end
|
145
|
+
block = block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
|
146
|
+
|
147
|
+
wrap_original(__method__, &block)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Decorates the stubbed method with the supplied block. The original
|
151
|
+
# unmodified method is passed to the block along with any method call
|
152
|
+
# arguments so you can delegate to it, whilst still being able to
|
153
|
+
# change what args are passed to it and/or change the return value.
|
154
|
+
#
|
155
|
+
# @note This is only available on partial doubles.
|
156
|
+
#
|
157
|
+
# @return [nil] No further chaining is supported after this.
|
158
|
+
# @example
|
159
|
+
# expect(api).to receive(:large_list).and_wrap_original do |original_method, *args, &block|
|
160
|
+
# original_method.call(*args, &block).first(10)
|
161
|
+
# end
|
162
|
+
def and_wrap_original(&block)
|
163
|
+
wrap_original(__method__, &block)
|
147
164
|
end
|
148
165
|
|
149
166
|
# @overload and_raise
|
@@ -153,8 +170,8 @@ module RSpec
|
|
153
170
|
#
|
154
171
|
# Tells the object to raise an exception when the message is received.
|
155
172
|
#
|
173
|
+
# @return [nil] No further chaining is supported after this.
|
156
174
|
# @note
|
157
|
-
#
|
158
175
|
# When you pass an exception class, the MessageExpectation will raise
|
159
176
|
# an instance of it, creating it with `exception` and passing `message`
|
160
177
|
# if specified. If the exception class initializer requires more than
|
@@ -162,17 +179,13 @@ module RSpec
|
|
162
179
|
# otherwise this method will raise an ArgumentError exception.
|
163
180
|
#
|
164
181
|
# @example
|
165
|
-
#
|
166
182
|
# allow(car).to receive(:go).and_raise
|
167
183
|
# allow(car).to receive(:go).and_raise(OutOfGas)
|
168
184
|
# allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive")
|
169
185
|
# allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz))
|
170
|
-
def and_raise(
|
171
|
-
|
172
|
-
|
173
|
-
end
|
174
|
-
|
175
|
-
self.terminal_implementation_action = Proc.new { raise exception }
|
186
|
+
def and_raise(*args)
|
187
|
+
raise_already_invoked_error_if_necessary(__method__)
|
188
|
+
self.terminal_implementation_action = Proc.new { raise(*args) }
|
176
189
|
nil
|
177
190
|
end
|
178
191
|
|
@@ -182,11 +195,12 @@ module RSpec
|
|
182
195
|
# Tells the object to throw a symbol (with the object if that form is
|
183
196
|
# used) when the message is received.
|
184
197
|
#
|
198
|
+
# @return [nil] No further chaining is supported after this.
|
185
199
|
# @example
|
186
|
-
#
|
187
200
|
# allow(car).to receive(:go).and_throw(:out_of_gas)
|
188
201
|
# allow(car).to receive(:go).and_throw(:out_of_gas, :level => 0.1)
|
189
202
|
def and_throw(*args)
|
203
|
+
raise_already_invoked_error_if_necessary(__method__)
|
190
204
|
self.terminal_implementation_action = Proc.new { throw(*args) }
|
191
205
|
nil
|
192
206
|
end
|
@@ -194,162 +208,33 @@ module RSpec
|
|
194
208
|
# Tells the object to yield one or more args to a block when the message
|
195
209
|
# is received.
|
196
210
|
#
|
211
|
+
# @return [MessageExpectation] self, to support further chaining.
|
197
212
|
# @example
|
198
|
-
#
|
199
213
|
# stream.stub(:open).and_yield(StringIO.new)
|
200
214
|
def and_yield(*args, &block)
|
215
|
+
raise_already_invoked_error_if_necessary(__method__)
|
201
216
|
yield @eval_context = Object.new if block
|
217
|
+
|
218
|
+
# Initialize args to yield now that it's being used, see also: comment
|
219
|
+
# in constructor.
|
220
|
+
@args_to_yield ||= []
|
221
|
+
|
202
222
|
@args_to_yield << args
|
203
223
|
self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
|
204
224
|
self
|
205
225
|
end
|
226
|
+
# @!endgroup
|
206
227
|
|
207
|
-
#
|
208
|
-
def matches?(message, *args)
|
209
|
-
@message == message && @argument_list_matcher.args_match?(*args)
|
210
|
-
end
|
211
|
-
|
212
|
-
# @private
|
213
|
-
def invoke(parent_stub, *args, &block)
|
214
|
-
invoke_incrementing_actual_calls_by(1, parent_stub, *args, &block)
|
215
|
-
end
|
216
|
-
|
217
|
-
# @private
|
218
|
-
def invoke_without_incrementing_received_count(parent_stub, *args, &block)
|
219
|
-
invoke_incrementing_actual_calls_by(0, parent_stub, *args, &block)
|
220
|
-
end
|
221
|
-
|
222
|
-
# @private
|
223
|
-
def negative?
|
224
|
-
@expected_received_count == 0 && !@at_least
|
225
|
-
end
|
226
|
-
|
227
|
-
# @private
|
228
|
-
def called_max_times?
|
229
|
-
@expected_received_count != :any &&
|
230
|
-
!@at_least &&
|
231
|
-
@expected_received_count > 0 &&
|
232
|
-
@actual_received_count >= @expected_received_count
|
233
|
-
end
|
234
|
-
|
235
|
-
# @private
|
236
|
-
def matches_name_but_not_args(message, *args)
|
237
|
-
@message == message and not @argument_list_matcher.args_match?(*args)
|
238
|
-
end
|
239
|
-
|
240
|
-
# @private
|
241
|
-
def verify_messages_received
|
242
|
-
InsertOntoBacktrace.line(@expected_from) do
|
243
|
-
generate_error unless expected_messages_received? || failed_fast?
|
244
|
-
end
|
245
|
-
end
|
246
|
-
|
247
|
-
# @private
|
248
|
-
def expected_messages_received?
|
249
|
-
ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
|
250
|
-
end
|
251
|
-
|
252
|
-
def ensure_expected_ordering_received!
|
253
|
-
@order_group.verify_invocation_order(self) if @ordered
|
254
|
-
true
|
255
|
-
end
|
256
|
-
|
257
|
-
# @private
|
258
|
-
def ignoring_args?
|
259
|
-
@expected_received_count == :any
|
260
|
-
end
|
261
|
-
|
262
|
-
# @private
|
263
|
-
def matches_at_least_count?
|
264
|
-
@at_least && @actual_received_count >= @expected_received_count
|
265
|
-
end
|
266
|
-
|
267
|
-
# @private
|
268
|
-
def matches_at_most_count?
|
269
|
-
@at_most && @actual_received_count <= @expected_received_count
|
270
|
-
end
|
271
|
-
|
272
|
-
# @private
|
273
|
-
def matches_exact_count?
|
274
|
-
@expected_received_count == @actual_received_count
|
275
|
-
end
|
276
|
-
|
277
|
-
# @private
|
278
|
-
def similar_messages
|
279
|
-
@similar_messages ||= []
|
280
|
-
end
|
281
|
-
|
282
|
-
# @private
|
283
|
-
def advise(*args)
|
284
|
-
similar_messages << args
|
285
|
-
end
|
286
|
-
|
287
|
-
# @private
|
288
|
-
def generate_error
|
289
|
-
if similar_messages.empty?
|
290
|
-
@error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args)
|
291
|
-
else
|
292
|
-
@error_generator.raise_similar_message_args_error(self, *@similar_messages)
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
def expectation_count_type
|
297
|
-
return :at_least if @at_least
|
298
|
-
return :at_most if @at_most
|
299
|
-
return nil
|
300
|
-
end
|
301
|
-
|
302
|
-
# @private
|
303
|
-
def description
|
304
|
-
@error_generator.describe_expectation(@message, @expected_received_count, @actual_received_count, *expected_args)
|
305
|
-
end
|
306
|
-
|
307
|
-
def raise_out_of_order_error
|
308
|
-
@error_generator.raise_out_of_order_error @message
|
309
|
-
end
|
310
|
-
|
311
|
-
# Constrains a stub or message expectation to invocations with specific
|
312
|
-
# arguments.
|
313
|
-
#
|
314
|
-
# With a stub, if the message might be received with other args as well,
|
315
|
-
# you should stub a default value first, and then stub or mock the same
|
316
|
-
# message using `with` to constrain to specific arguments.
|
317
|
-
#
|
318
|
-
# A message expectation will fail if the message is received with different
|
319
|
-
# arguments.
|
320
|
-
#
|
321
|
-
# @example
|
322
|
-
#
|
323
|
-
# allow(cart).to receive(:add) { :failure }
|
324
|
-
# allow(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
|
325
|
-
# cart.add(Book.new(:isbn => 1234567890))
|
326
|
-
# # => :failure
|
327
|
-
# cart.add(Book.new(:isbn => 1934356379))
|
328
|
-
# # => :success
|
329
|
-
#
|
330
|
-
# expect(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
|
331
|
-
# cart.add(Book.new(:isbn => 1234567890))
|
332
|
-
# # => failed expectation
|
333
|
-
# cart.add(Book.new(:isbn => 1934356379))
|
334
|
-
# # => passes
|
335
|
-
def with(*args, &block)
|
336
|
-
if args.empty?
|
337
|
-
raise ArgumentError,
|
338
|
-
"`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
|
339
|
-
end
|
340
|
-
|
341
|
-
self.inner_implementation_action = block
|
342
|
-
@argument_list_matcher = ArgumentListMatcher.new(*args)
|
343
|
-
self
|
344
|
-
end
|
228
|
+
# @!group Constraining Receive Counts
|
345
229
|
|
346
230
|
# Constrain a message expectation to be received a specific number of
|
347
231
|
# times.
|
348
232
|
#
|
233
|
+
# @return [MessageExpectation] self, to support further chaining.
|
349
234
|
# @example
|
350
|
-
#
|
351
235
|
# expect(dealer).to receive(:deal_card).exactly(10).times
|
352
236
|
def exactly(n, &block)
|
237
|
+
raise_already_invoked_error_if_necessary(__method__)
|
353
238
|
self.inner_implementation_action = block
|
354
239
|
set_expected_received_count :exactly, n
|
355
240
|
self
|
@@ -358,10 +243,11 @@ module RSpec
|
|
358
243
|
# Constrain a message expectation to be received at least a specific
|
359
244
|
# number of times.
|
360
245
|
#
|
246
|
+
# @return [MessageExpectation] self, to support further chaining.
|
361
247
|
# @example
|
362
|
-
#
|
363
248
|
# expect(dealer).to receive(:deal_card).at_least(9).times
|
364
249
|
def at_least(n, &block)
|
250
|
+
raise_already_invoked_error_if_necessary(__method__)
|
365
251
|
set_expected_received_count :at_least, n
|
366
252
|
|
367
253
|
if n == 0
|
@@ -376,10 +262,11 @@ module RSpec
|
|
376
262
|
# Constrain a message expectation to be received at most a specific
|
377
263
|
# number of times.
|
378
264
|
#
|
265
|
+
# @return [MessageExpectation] self, to support further chaining.
|
379
266
|
# @example
|
380
|
-
#
|
381
267
|
# expect(dealer).to receive(:deal_card).at_most(10).times
|
382
268
|
def at_most(n, &block)
|
269
|
+
raise_already_invoked_error_if_necessary(__method__)
|
383
270
|
self.inner_implementation_action = block
|
384
271
|
set_expected_received_count :at_most, n
|
385
272
|
self
|
@@ -387,8 +274,8 @@ module RSpec
|
|
387
274
|
|
388
275
|
# Syntactic sugar for `exactly`, `at_least` and `at_most`
|
389
276
|
#
|
277
|
+
# @return [MessageExpectation] self, to support further chaining.
|
390
278
|
# @example
|
391
|
-
#
|
392
279
|
# expect(dealer).to receive(:deal_card).exactly(10).times
|
393
280
|
# expect(dealer).to receive(:deal_card).at_least(10).times
|
394
281
|
# expect(dealer).to receive(:deal_card).at_most(10).times
|
@@ -396,22 +283,23 @@ module RSpec
|
|
396
283
|
self.inner_implementation_action = block
|
397
284
|
self
|
398
285
|
end
|
286
|
+
alias time times
|
399
287
|
|
400
288
|
# Expect a message not to be received at all.
|
401
289
|
#
|
290
|
+
# @return [MessageExpectation] self, to support further chaining.
|
402
291
|
# @example
|
403
|
-
#
|
404
292
|
# expect(car).to receive(:stop).never
|
405
293
|
def never
|
406
|
-
|
294
|
+
error_generator.raise_double_negation_error("expect(obj)") if negative?
|
407
295
|
@expected_received_count = 0
|
408
296
|
self
|
409
297
|
end
|
410
298
|
|
411
299
|
# Expect a message to be received exactly one time.
|
412
300
|
#
|
301
|
+
# @return [MessageExpectation] self, to support further chaining.
|
413
302
|
# @example
|
414
|
-
#
|
415
303
|
# expect(car).to receive(:go).once
|
416
304
|
def once(&block)
|
417
305
|
self.inner_implementation_action = block
|
@@ -421,8 +309,8 @@ module RSpec
|
|
421
309
|
|
422
310
|
# Expect a message to be received exactly two times.
|
423
311
|
#
|
312
|
+
# @return [MessageExpectation] self, to support further chaining.
|
424
313
|
# @example
|
425
|
-
#
|
426
314
|
# expect(car).to receive(:go).twice
|
427
315
|
def twice(&block)
|
428
316
|
self.inner_implementation_action = block
|
@@ -430,14 +318,72 @@ module RSpec
|
|
430
318
|
self
|
431
319
|
end
|
432
320
|
|
433
|
-
# Expect
|
321
|
+
# Expect a message to be received exactly three times.
|
434
322
|
#
|
323
|
+
# @return [MessageExpectation] self, to support further chaining.
|
435
324
|
# @example
|
325
|
+
# expect(car).to receive(:go).thrice
|
326
|
+
def thrice(&block)
|
327
|
+
self.inner_implementation_action = block
|
328
|
+
set_expected_received_count :exactly, 3
|
329
|
+
self
|
330
|
+
end
|
331
|
+
# @!endgroup
|
332
|
+
|
333
|
+
# @!group Other Constraints
|
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
|
+
# @return [MessageExpectation] self, to support further chaining.
|
346
|
+
# @example
|
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
|
+
raise_already_invoked_error_if_necessary(__method__)
|
361
|
+
if args.empty?
|
362
|
+
raise ArgumentError,
|
363
|
+
"`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
|
364
|
+
end
|
365
|
+
|
366
|
+
self.inner_implementation_action = block
|
367
|
+
@argument_list_matcher = ArgumentListMatcher.new(*args)
|
368
|
+
self
|
369
|
+
end
|
370
|
+
ruby2_keywords(:with) if respond_to?(:ruby2_keywords, true)
|
371
|
+
|
372
|
+
# Expect messages to be received in a specific order.
|
436
373
|
#
|
374
|
+
# @return [MessageExpectation] self, to support further chaining.
|
375
|
+
# @example
|
437
376
|
# expect(api).to receive(:prepare).ordered
|
438
377
|
# expect(api).to receive(:run).ordered
|
439
378
|
# expect(api).to receive(:finish).ordered
|
440
379
|
def ordered(&block)
|
380
|
+
if type == :stub
|
381
|
+
RSpec.warning(
|
382
|
+
"`allow(...).to receive(..).ordered` is not supported and will " \
|
383
|
+
"have no effect, use `and_return(*ordered_values)` instead."
|
384
|
+
)
|
385
|
+
end
|
386
|
+
|
441
387
|
self.inner_implementation_action = block
|
442
388
|
additional_expected_calls.times do
|
443
389
|
@order_group.register(self)
|
@@ -446,96 +392,301 @@ module RSpec
|
|
446
392
|
self
|
447
393
|
end
|
448
394
|
|
449
|
-
# @
|
450
|
-
def
|
451
|
-
|
452
|
-
|
395
|
+
# @return [String] a nice representation of the message expectation
|
396
|
+
def to_s
|
397
|
+
args_description = error_generator.method_call_args_description(@argument_list_matcher.expected_args, "", "") { true }
|
398
|
+
args_description = "(#{args_description})" unless args_description.start_with?("(")
|
399
|
+
"#<#{self.class} #{error_generator.intro}.#{message}#{args_description}>"
|
453
400
|
end
|
454
|
-
|
401
|
+
alias inspect to_s
|
455
402
|
|
456
403
|
# @private
|
457
|
-
|
458
|
-
|
459
|
-
|
404
|
+
# Contains the parts of `MessageExpectation` that aren't part of
|
405
|
+
# rspec-mocks' public API. The class is very big and could really use
|
406
|
+
# some collaborators it delegates to for this stuff but for now this was
|
407
|
+
# the simplest way to split the public from private stuff to make it
|
408
|
+
# easier to publish the docs for the APIs we want published.
|
409
|
+
module ImplementationDetails
|
410
|
+
attr_accessor :error_generator, :implementation
|
411
|
+
attr_reader :message
|
412
|
+
attr_reader :orig_object
|
413
|
+
attr_writer :expected_received_count, :expected_from, :argument_list_matcher
|
414
|
+
protected :expected_received_count=, :expected_from=, :error_generator=, :implementation=
|
415
|
+
|
416
|
+
# @private
|
417
|
+
attr_reader :type
|
418
|
+
|
419
|
+
# rubocop:disable Metrics/ParameterLists
|
420
|
+
def initialize(error_generator, expectation_ordering, expected_from, method_double,
|
421
|
+
type=:expectation, opts={}, &implementation_block)
|
422
|
+
@type = type
|
423
|
+
@error_generator = error_generator
|
424
|
+
@error_generator.opts = error_generator.opts.merge(opts)
|
425
|
+
@expected_from = expected_from
|
426
|
+
@method_double = method_double
|
427
|
+
@orig_object = @method_double.object
|
428
|
+
@message = @method_double.method_name
|
429
|
+
@actual_received_count = 0
|
430
|
+
@actual_received_count_write_mutex = Support::Mutex.new
|
431
|
+
@expected_received_count = type == :expectation ? 1 : :any
|
432
|
+
@argument_list_matcher = ArgumentListMatcher::MATCH_ALL
|
433
|
+
@order_group = expectation_ordering
|
434
|
+
@order_group.register(self) unless type == :stub
|
435
|
+
@expectation_type = type
|
436
|
+
@ordered = false
|
437
|
+
@at_least = @at_most = @exactly = nil
|
438
|
+
|
439
|
+
# Initialized to nil so that we don't allocate an array for every
|
440
|
+
# mock or stub. See also comment in `and_yield`.
|
441
|
+
@args_to_yield = nil
|
442
|
+
@eval_context = nil
|
443
|
+
@yield_receiver_to_implementation_block = false
|
460
444
|
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
445
|
+
@implementation = Implementation.new
|
446
|
+
self.inner_implementation_action = implementation_block
|
447
|
+
end
|
448
|
+
# rubocop:enable Metrics/ParameterLists
|
465
449
|
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
end
|
450
|
+
def expected_args
|
451
|
+
@argument_list_matcher.expected_args
|
452
|
+
end
|
470
453
|
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
454
|
+
def and_yield_receiver_to_implementation
|
455
|
+
@yield_receiver_to_implementation_block = true
|
456
|
+
self
|
457
|
+
end
|
475
458
|
|
476
|
-
|
459
|
+
def yield_receiver_to_implementation_block?
|
460
|
+
@yield_receiver_to_implementation_block
|
461
|
+
end
|
462
|
+
|
463
|
+
def matches?(message, *args)
|
464
|
+
@message == message && @argument_list_matcher.args_match?(*args)
|
465
|
+
end
|
466
|
+
ruby2_keywords :matches? if respond_to?(:ruby2_keywords, true)
|
467
|
+
|
468
|
+
def safe_invoke(parent_stub, *args, &block)
|
469
|
+
invoke_incrementing_actual_calls_by(1, false, parent_stub, *args, &block)
|
470
|
+
end
|
471
|
+
ruby2_keywords :safe_invoke if respond_to?(:ruby2_keywords, true)
|
472
|
+
|
473
|
+
def invoke(parent_stub, *args, &block)
|
474
|
+
invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block)
|
475
|
+
end
|
476
|
+
ruby2_keywords :invoke if respond_to?(:ruby2_keywords, true)
|
477
|
+
|
478
|
+
def invoke_without_incrementing_received_count(parent_stub, *args, &block)
|
479
|
+
invoke_incrementing_actual_calls_by(0, true, parent_stub, *args, &block)
|
480
|
+
end
|
481
|
+
ruby2_keywords :invoke_without_incrementing_received_count if respond_to?(:ruby2_keywords, true)
|
482
|
+
|
483
|
+
def negative?
|
484
|
+
@expected_received_count == 0 && !@at_least
|
485
|
+
end
|
486
|
+
|
487
|
+
def called_max_times?
|
488
|
+
@expected_received_count != :any &&
|
489
|
+
!@at_least &&
|
490
|
+
@expected_received_count > 0 &&
|
491
|
+
@actual_received_count >= @expected_received_count
|
492
|
+
end
|
493
|
+
|
494
|
+
def matches_name_but_not_args(message, *args)
|
495
|
+
@message == message && !@argument_list_matcher.args_match?(*args)
|
496
|
+
end
|
497
|
+
|
498
|
+
def verify_messages_received
|
499
|
+
return if expected_messages_received?
|
500
|
+
generate_error
|
501
|
+
end
|
502
|
+
|
503
|
+
def expected_messages_received?
|
504
|
+
ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
|
505
|
+
end
|
477
506
|
|
478
|
-
|
479
|
-
|
480
|
-
|
507
|
+
def ensure_expected_ordering_received!
|
508
|
+
@order_group.verify_invocation_order(self) if @ordered
|
509
|
+
true
|
481
510
|
end
|
482
511
|
|
483
|
-
|
484
|
-
@
|
485
|
-
@failed_fast = true
|
486
|
-
#args are the args we actually received, @argument_list_matcher is the
|
487
|
-
#list of args we were expecting
|
488
|
-
@error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args)
|
512
|
+
def ignoring_args?
|
513
|
+
@expected_received_count == :any
|
489
514
|
end
|
490
515
|
|
491
|
-
|
516
|
+
def matches_at_least_count?
|
517
|
+
@at_least && @actual_received_count >= @expected_received_count
|
518
|
+
end
|
519
|
+
|
520
|
+
def matches_at_most_count?
|
521
|
+
@at_most && @actual_received_count <= @expected_received_count
|
522
|
+
end
|
523
|
+
|
524
|
+
def matches_exact_count?
|
525
|
+
@expected_received_count == @actual_received_count
|
526
|
+
end
|
527
|
+
|
528
|
+
def similar_messages
|
529
|
+
@similar_messages ||= []
|
530
|
+
end
|
531
|
+
|
532
|
+
def advise(*args)
|
533
|
+
similar_messages << args
|
534
|
+
end
|
535
|
+
|
536
|
+
def unadvise(args)
|
537
|
+
similar_messages.delete_if { |message| args.include?(message) }
|
538
|
+
end
|
539
|
+
|
540
|
+
def generate_error
|
541
|
+
if similar_messages.empty?
|
542
|
+
@error_generator.raise_expectation_error(
|
543
|
+
@message, @expected_received_count, @argument_list_matcher,
|
544
|
+
@actual_received_count, expectation_count_type, expected_args,
|
545
|
+
@expected_from, exception_source_id
|
546
|
+
)
|
547
|
+
else
|
548
|
+
@error_generator.raise_similar_message_args_error(
|
549
|
+
self, @similar_messages, @expected_from
|
550
|
+
)
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
def raise_unexpected_message_args_error(args_for_multiple_calls)
|
555
|
+
@error_generator.raise_unexpected_message_args_error(self, args_for_multiple_calls, exception_source_id)
|
556
|
+
end
|
557
|
+
|
558
|
+
def expectation_count_type
|
559
|
+
return :at_least if @at_least
|
560
|
+
return :at_most if @at_most
|
561
|
+
nil
|
562
|
+
end
|
563
|
+
|
564
|
+
def description_for(verb)
|
565
|
+
@error_generator.describe_expectation(
|
566
|
+
verb, @message, @expected_received_count,
|
567
|
+
@actual_received_count, expected_args
|
568
|
+
)
|
569
|
+
end
|
570
|
+
|
571
|
+
def raise_out_of_order_error
|
572
|
+
@error_generator.raise_out_of_order_error @message
|
573
|
+
end
|
574
|
+
|
575
|
+
def additional_expected_calls
|
576
|
+
return 0 if @expectation_type == :stub || !@exactly
|
577
|
+
@expected_received_count - 1
|
578
|
+
end
|
579
|
+
|
580
|
+
def ordered?
|
581
|
+
@ordered
|
582
|
+
end
|
583
|
+
|
584
|
+
def negative_expectation_for?(message)
|
585
|
+
@message == message && negative?
|
586
|
+
end
|
587
|
+
|
588
|
+
def actual_received_count_matters?
|
589
|
+
@at_least || @at_most || @exactly
|
590
|
+
end
|
591
|
+
|
592
|
+
def increase_actual_received_count!
|
593
|
+
@actual_received_count_write_mutex.synchronize do
|
594
|
+
@actual_received_count += 1
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
private
|
599
|
+
|
600
|
+
def exception_source_id
|
601
|
+
@exception_source_id ||= "#{self.class.name} #{__id__}"
|
602
|
+
end
|
603
|
+
|
604
|
+
def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block)
|
605
|
+
args.unshift(orig_object) if yield_receiver_to_implementation_block?
|
606
|
+
|
607
|
+
if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count))
|
608
|
+
# args are the args we actually received, @argument_list_matcher is the
|
609
|
+
# list of args we were expecting
|
610
|
+
@error_generator.raise_expectation_error(
|
611
|
+
@message, @expected_received_count,
|
612
|
+
@argument_list_matcher,
|
613
|
+
@actual_received_count + increment,
|
614
|
+
expectation_count_type, args, nil, exception_source_id
|
615
|
+
)
|
616
|
+
end
|
617
|
+
|
618
|
+
@order_group.handle_order_constraint self
|
492
619
|
|
493
|
-
begin
|
494
620
|
if implementation.present?
|
495
621
|
implementation.call(*args, &block)
|
496
622
|
elsif parent_stub
|
497
623
|
parent_stub.invoke(nil, *args, &block)
|
498
624
|
end
|
499
625
|
ensure
|
500
|
-
@
|
626
|
+
@actual_received_count_write_mutex.synchronize do
|
627
|
+
@actual_received_count += increment
|
628
|
+
end
|
501
629
|
end
|
502
|
-
|
630
|
+
ruby2_keywords :invoke_incrementing_actual_calls_by if respond_to?(:ruby2_keywords, true)
|
503
631
|
|
504
|
-
|
505
|
-
|
506
|
-
|
632
|
+
def has_been_invoked?
|
633
|
+
@actual_received_count > 0
|
634
|
+
end
|
507
635
|
|
508
|
-
|
509
|
-
|
510
|
-
@at_most = (relativity == :at_most)
|
511
|
-
@exactly = (relativity == :exactly)
|
512
|
-
@expected_received_count = case n
|
513
|
-
when Numeric then n
|
514
|
-
when :once then 1
|
515
|
-
when :twice then 2
|
516
|
-
end
|
517
|
-
end
|
636
|
+
def raise_already_invoked_error_if_necessary(calling_customization)
|
637
|
+
return unless has_been_invoked?
|
518
638
|
|
519
|
-
|
520
|
-
|
521
|
-
end
|
639
|
+
error_generator.raise_already_invoked_error(message, calling_customization)
|
640
|
+
end
|
522
641
|
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
642
|
+
def set_expected_received_count(relativity, n)
|
643
|
+
raise "`count` is not supported with negative message expectations" if negative?
|
644
|
+
@at_least = (relativity == :at_least)
|
645
|
+
@at_most = (relativity == :at_most)
|
646
|
+
@exactly = (relativity == :exactly)
|
647
|
+
@expected_received_count = case n
|
648
|
+
when Numeric then n
|
649
|
+
when :once then 1
|
650
|
+
when :twice then 2
|
651
|
+
when :thrice then 3
|
652
|
+
end
|
653
|
+
end
|
528
654
|
|
529
|
-
|
530
|
-
|
531
|
-
|
655
|
+
def initial_implementation_action=(action)
|
656
|
+
implementation.initial_action = action
|
657
|
+
end
|
532
658
|
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
659
|
+
def inner_implementation_action=(action)
|
660
|
+
return unless action
|
661
|
+
warn_about_stub_override if implementation.inner_action
|
662
|
+
implementation.inner_action = action
|
663
|
+
end
|
664
|
+
|
665
|
+
def terminal_implementation_action=(action)
|
666
|
+
implementation.terminal_action = action
|
667
|
+
end
|
668
|
+
|
669
|
+
def warn_about_stub_override
|
670
|
+
RSpec.warning(
|
671
|
+
"You're overriding a previous stub implementation of `#{@message}`. " \
|
672
|
+
"Called from #{CallerFilter.first_non_rspec_line}."
|
673
|
+
)
|
674
|
+
end
|
675
|
+
|
676
|
+
def wrap_original(method_name, &block)
|
677
|
+
if RSpec::Mocks::TestDouble === @method_double.object
|
678
|
+
@error_generator.raise_only_valid_on_a_partial_double(method_name)
|
679
|
+
else
|
680
|
+
warn_about_stub_override if implementation.inner_action
|
681
|
+
@implementation = AndWrapOriginalImplementation.new(@method_double.original_implementation_callable, block)
|
682
|
+
@yield_receiver_to_implementation_block = false
|
683
|
+
end
|
684
|
+
|
685
|
+
nil
|
686
|
+
end
|
538
687
|
end
|
688
|
+
|
689
|
+
include ImplementationDetails
|
539
690
|
end
|
540
691
|
|
541
692
|
# Handles the implementation of an `and_yield` declaration.
|
@@ -547,7 +698,7 @@ module RSpec
|
|
547
698
|
@error_generator = error_generator
|
548
699
|
end
|
549
700
|
|
550
|
-
def call(*
|
701
|
+
def call(*_args_to_ignore, &block)
|
551
702
|
return if @args_to_yield.empty? && @eval_context.nil?
|
552
703
|
|
553
704
|
@error_generator.raise_missing_block_error @args_to_yield unless block
|
@@ -555,11 +706,11 @@ module RSpec
|
|
555
706
|
block_signature = Support::BlockSignature.new(block)
|
556
707
|
|
557
708
|
@args_to_yield.each do |args|
|
558
|
-
unless Support::
|
709
|
+
unless Support::StrictSignatureVerifier.new(block_signature, args).valid?
|
559
710
|
@error_generator.raise_wrong_arity_error(args, block_signature)
|
560
711
|
end
|
561
712
|
|
562
|
-
value = @eval_context ? @eval_context.instance_exec(*args, &block) :
|
713
|
+
value = @eval_context ? @eval_context.instance_exec(*args, &block) : yield(*args)
|
563
714
|
end
|
564
715
|
value
|
565
716
|
end
|
@@ -572,7 +723,7 @@ module RSpec
|
|
572
723
|
@values_to_return = values_to_return
|
573
724
|
end
|
574
725
|
|
575
|
-
def call(*
|
726
|
+
def call(*_args_to_ignore, &_block)
|
576
727
|
if @values_to_return.size > 1
|
577
728
|
@values_to_return.shift
|
578
729
|
else
|
@@ -581,6 +732,24 @@ module RSpec
|
|
581
732
|
end
|
582
733
|
end
|
583
734
|
|
735
|
+
# Handles the implementation of an `and_invoke` implementation.
|
736
|
+
# @private
|
737
|
+
class AndInvokeImplementation
|
738
|
+
def initialize(procs_to_invoke)
|
739
|
+
@procs_to_invoke = procs_to_invoke
|
740
|
+
end
|
741
|
+
|
742
|
+
def call(*args, &block)
|
743
|
+
proc = if @procs_to_invoke.size > 1
|
744
|
+
@procs_to_invoke.shift
|
745
|
+
else
|
746
|
+
@procs_to_invoke.first
|
747
|
+
end
|
748
|
+
|
749
|
+
proc.call(*args, &block)
|
750
|
+
end
|
751
|
+
end
|
752
|
+
|
584
753
|
# Represents a configured implementation. Takes into account
|
585
754
|
# any number of sub-implementations.
|
586
755
|
# @private
|
@@ -592,6 +761,7 @@ module RSpec
|
|
592
761
|
action.call(*args, &block)
|
593
762
|
end.last
|
594
763
|
end
|
764
|
+
ruby2_keywords :call if respond_to?(:ruby2_keywords, true)
|
595
765
|
|
596
766
|
def present?
|
597
767
|
actions.any?
|
@@ -606,22 +776,23 @@ module RSpec
|
|
606
776
|
|
607
777
|
# Represents an `and_call_original` implementation.
|
608
778
|
# @private
|
609
|
-
class
|
610
|
-
def initialize(method)
|
779
|
+
class AndWrapOriginalImplementation
|
780
|
+
def initialize(method, block)
|
611
781
|
@method = method
|
782
|
+
@block = block
|
612
783
|
end
|
613
784
|
|
614
785
|
CannotModifyFurtherError = Class.new(StandardError)
|
615
786
|
|
616
|
-
def initial_action=(
|
787
|
+
def initial_action=(_value)
|
617
788
|
raise cannot_modify_further_error
|
618
789
|
end
|
619
790
|
|
620
|
-
def inner_action=(
|
791
|
+
def inner_action=(_value)
|
621
792
|
raise cannot_modify_further_error
|
622
793
|
end
|
623
794
|
|
624
|
-
def terminal_action=(
|
795
|
+
def terminal_action=(_value)
|
625
796
|
raise cannot_modify_further_error
|
626
797
|
end
|
627
798
|
|
@@ -634,28 +805,16 @@ module RSpec
|
|
634
805
|
end
|
635
806
|
|
636
807
|
def call(*args, &block)
|
637
|
-
@
|
808
|
+
@block.call(@method, *args, &block)
|
638
809
|
end
|
810
|
+
ruby2_keywords :call if respond_to?(:ruby2_keywords, true)
|
639
811
|
|
640
812
|
private
|
641
813
|
|
642
814
|
def cannot_modify_further_error
|
643
|
-
CannotModifyFurtherError.new "This method has already been configured "
|
815
|
+
CannotModifyFurtherError.new "This method has already been configured " \
|
644
816
|
"to call the original implementation, and cannot be modified further."
|
645
817
|
end
|
646
818
|
end
|
647
|
-
|
648
|
-
# Insert original locations into stacktraces
|
649
|
-
#
|
650
|
-
# @private
|
651
|
-
class InsertOntoBacktrace
|
652
|
-
def self.line(location)
|
653
|
-
yield
|
654
|
-
rescue RSpec::Mocks::MockExpectationError => error
|
655
|
-
error.backtrace.insert(0, location)
|
656
|
-
Kernel::raise error
|
657
|
-
end
|
658
|
-
end
|
659
|
-
|
660
819
|
end
|
661
820
|
end
|