rspec-mocks 3.0.4 → 3.12.6

Sign up to get free protection for your applications and to get access to all the features.
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