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.
Files changed (49) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +1 -1
  5. data/Changelog.md +512 -2
  6. data/{License.txt → LICENSE.md} +5 -4
  7. data/README.md +113 -30
  8. data/lib/rspec/mocks/any_instance/chain.rb +5 -3
  9. data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
  10. data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +1 -5
  11. data/lib/rspec/mocks/any_instance/expectation_chain.rb +9 -8
  12. data/lib/rspec/mocks/any_instance/message_chains.rb +7 -8
  13. data/lib/rspec/mocks/any_instance/proxy.rb +14 -5
  14. data/lib/rspec/mocks/any_instance/recorder.rb +61 -31
  15. data/lib/rspec/mocks/any_instance/stub_chain.rb +15 -11
  16. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +1 -5
  17. data/lib/rspec/mocks/any_instance.rb +1 -0
  18. data/lib/rspec/mocks/argument_list_matcher.rb +55 -10
  19. data/lib/rspec/mocks/argument_matchers.rb +88 -30
  20. data/lib/rspec/mocks/configuration.rb +61 -13
  21. data/lib/rspec/mocks/error_generator.rb +250 -107
  22. data/lib/rspec/mocks/example_methods.rb +151 -28
  23. data/lib/rspec/mocks/instance_method_stasher.rb +17 -6
  24. data/lib/rspec/mocks/matchers/have_received.rb +50 -20
  25. data/lib/rspec/mocks/matchers/receive.rb +39 -11
  26. data/lib/rspec/mocks/matchers/receive_message_chain.rb +22 -7
  27. data/lib/rspec/mocks/matchers/receive_messages.rb +12 -7
  28. data/lib/rspec/mocks/message_chain.rb +3 -7
  29. data/lib/rspec/mocks/message_expectation.rb +466 -307
  30. data/lib/rspec/mocks/method_double.rb +88 -29
  31. data/lib/rspec/mocks/method_reference.rb +85 -25
  32. data/lib/rspec/mocks/minitest_integration.rb +68 -0
  33. data/lib/rspec/mocks/mutate_const.rb +50 -109
  34. data/lib/rspec/mocks/object_reference.rb +89 -32
  35. data/lib/rspec/mocks/order_group.rb +4 -5
  36. data/lib/rspec/mocks/proxy.rb +156 -60
  37. data/lib/rspec/mocks/space.rb +52 -35
  38. data/lib/rspec/mocks/standalone.rb +1 -1
  39. data/lib/rspec/mocks/syntax.rb +26 -30
  40. data/lib/rspec/mocks/targets.rb +55 -28
  41. data/lib/rspec/mocks/test_double.rb +43 -7
  42. data/lib/rspec/mocks/verifying_double.rb +27 -33
  43. data/lib/rspec/mocks/{verifying_message_expecation.rb → verifying_message_expectation.rb} +11 -16
  44. data/lib/rspec/mocks/verifying_proxy.rb +77 -26
  45. data/lib/rspec/mocks/version.rb +1 -1
  46. data/lib/rspec/mocks.rb +8 -1
  47. data.tar.gz.sig +0 -0
  48. metadata +80 -43
  49. 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
- # MessageExpecation, this version is much faster and so can be used as an
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
- InsertOntoBacktrace.line(@backtrace_line) do
32
- unless @received
33
- @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil)
34
- end
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
- # @private
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
- # @private
75
- def expected_args
76
- @argument_list_matcher.expected_args
77
- end
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 received for every subsequent call.
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 and @at_least)
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
- def and_yield_receiver_to_implementation
120
- @yield_receiver_to_implementation_block = true
121
- self
122
- end
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
- def yield_receiver_to_implementation_block?
125
- @yield_receiver_to_implementation_block
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
- if RSpec::Mocks::TestDouble === @method_double.object
141
- @error_generator.raise_only_valid_on_a_partial_double(:and_call_original)
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(exception = RuntimeError, message = nil)
171
- if exception.respond_to?(:exception)
172
- exception = message ? exception.exception(message) : exception.exception
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
- # @private
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
- ErrorGenerator.raise_double_negation_error("expect(obj)") if negative?
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 messages to be received in a specific order.
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
- # @private
450
- def additional_expected_calls
451
- return 0 if @expectation_type == :stub || !@exactly
452
- @expected_received_count - 1
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
- def ordered?
458
- @ordered
459
- end
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
- # @private
462
- def negative_expectation_for?(message)
463
- @message == message && negative?
464
- end
445
+ @implementation = Implementation.new
446
+ self.inner_implementation_action = implementation_block
447
+ end
448
+ # rubocop:enable Metrics/ParameterLists
465
449
 
466
- # @private
467
- def actual_received_count_matters?
468
- @at_least || @at_most || @exactly
469
- end
450
+ def expected_args
451
+ @argument_list_matcher.expected_args
452
+ end
470
453
 
471
- # @private
472
- def increase_actual_received_count!
473
- @actual_received_count += 1
474
- end
454
+ def and_yield_receiver_to_implementation
455
+ @yield_receiver_to_implementation_block = true
456
+ self
457
+ end
475
458
 
476
- private
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
- def invoke_incrementing_actual_calls_by(increment, parent_stub, *args, &block)
479
- if yield_receiver_to_implementation_block?
480
- args.unshift(orig_object)
507
+ def ensure_expected_ordering_received!
508
+ @order_group.verify_invocation_order(self) if @ordered
509
+ true
481
510
  end
482
511
 
483
- if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
484
- @actual_received_count += increment
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
- @order_group.handle_order_constraint self
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
- @actual_received_count += increment
626
+ @actual_received_count_write_mutex.synchronize do
627
+ @actual_received_count += increment
628
+ end
501
629
  end
502
- end
630
+ ruby2_keywords :invoke_incrementing_actual_calls_by if respond_to?(:ruby2_keywords, true)
503
631
 
504
- def failed_fast?
505
- @failed_fast
506
- end
632
+ def has_been_invoked?
633
+ @actual_received_count > 0
634
+ end
507
635
 
508
- def set_expected_received_count(relativity, n)
509
- @at_least = (relativity == :at_least)
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
- def initial_implementation_action=(action)
520
- implementation.initial_action = action
521
- end
639
+ error_generator.raise_already_invoked_error(message, calling_customization)
640
+ end
522
641
 
523
- def inner_implementation_action=(action)
524
- return unless action
525
- warn_about_stub_override if implementation.inner_action
526
- implementation.inner_action = action
527
- end
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
- def terminal_implementation_action=(action)
530
- implementation.terminal_action = action
531
- end
655
+ def initial_implementation_action=(action)
656
+ implementation.initial_action = action
657
+ end
532
658
 
533
- def warn_about_stub_override
534
- RSpec.warning(
535
- "You're overriding a previous stub implementation of `#{@message}`. " +
536
- "Called from #{CallerFilter.first_non_rspec_line}."
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(*args_to_ignore, &block)
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::MethodSignatureVerifier.new(block_signature, args).valid?
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) : block.call(*args)
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(*args_to_ignore, &block)
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 AndCallOriginalImplementation
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=(value)
787
+ def initial_action=(_value)
617
788
  raise cannot_modify_further_error
618
789
  end
619
790
 
620
- def inner_action=(value)
791
+ def inner_action=(_value)
621
792
  raise cannot_modify_further_error
622
793
  end
623
794
 
624
- def terminal_action=(value)
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
- @method.call(*args, &block)
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