rspec-mocks 3.2.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -396,13 +396,10 @@ module RSpec
396
396
  if RSpec::Mocks.configuration.verify_doubled_constant_names? &&
397
397
  !ref.defined?
398
398
 
399
- raise VerifyingDoubleNotDefinedError,
400
- "#{ref.description.inspect} is not a defined constant. " \
401
- "Perhaps you misspelt it? " \
402
- "Disable check with `verify_doubled_constant_names` configuration option."
399
+ RSpec::Mocks.error_generator.raise_verifying_double_not_defined_error(ref)
403
400
  end
404
401
 
405
- RSpec::Mocks.configuration.verifying_double_declaration_callbacks.each do |block|
402
+ RSpec::Mocks.configuration.verifying_double_callbacks.each do |block|
406
403
  block.call(ref)
407
404
  end
408
405
 
@@ -69,9 +69,7 @@ module RSpec
69
69
  private
70
70
 
71
71
  def disallow(type, reason="")
72
- raise RSpec::Mocks::MockExpectationError,
73
- "Using #{type}(...) with the `have_received` " \
74
- "matcher is not supported#{reason}."
72
+ RSpec::Mocks.error_generator.raise_have_received_disallowed(type, reason)
75
73
  end
76
74
 
77
75
  def expect
@@ -88,8 +86,7 @@ module RSpec
88
86
 
89
87
  def ensure_count_unconstrained
90
88
  return unless count_constraint
91
- raise RSpec::Mocks::MockExpectationError,
92
- "can't use #{count_constraint} when negative"
89
+ RSpec::Mocks.error_generator.raise_cant_constrain_count_for_negated_have_received_error(count_constraint)
93
90
  end
94
91
 
95
92
  def count_constraint
@@ -99,10 +96,10 @@ module RSpec
99
96
  end
100
97
 
101
98
  def generate_failure_message
102
- mock_proxy.check_for_unexpected_arguments(@expectation)
103
- @expectation.generate_error
104
- rescue RSpec::Mocks::MockExpectationError => error
105
- error.message
99
+ RSpec::Support.with_failure_notifier(Proc.new { |err, _opt| return err.message }) do
100
+ mock_proxy.check_for_unexpected_arguments(@expectation)
101
+ @expectation.generate_error
102
+ end
106
103
  end
107
104
 
108
105
  def expected_messages_received_in_order?
@@ -30,10 +30,6 @@ module RSpec
30
30
 
31
31
  private
32
32
 
33
- def expectation(_object, _message, &_return_block)
34
- raise NotImplementedError
35
- end
36
-
37
33
  def chain_on(object, *chain, &block)
38
34
  initialize(object, *chain, &block)
39
35
  setup_chain
@@ -26,11 +26,13 @@ module RSpec
26
26
  end
27
27
 
28
28
  def verify_messages_received
29
- InsertOntoBacktrace.line(@backtrace_line) do
30
- unless @received
31
- @error_generator.raise_expectation_error(@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil)
32
- end
33
- end
29
+ return if @received
30
+ @error_generator.raise_expectation_error(
31
+ @message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil, [], @backtrace_line
32
+ )
33
+ end
34
+
35
+ def unadvise(_)
34
36
  end
35
37
  end
36
38
 
@@ -115,7 +117,7 @@ module RSpec
115
117
  @error_generator.raise_only_valid_on_a_partial_double(:and_call_original)
116
118
  else
117
119
  warn_about_stub_override if implementation.inner_action
118
- @implementation = AndWrapOriginalImplementation.new(@method_double.original_method, block)
120
+ @implementation = AndWrapOriginalImplementation.new(@method_double.original_implementation_callable, block)
119
121
  @yield_receiver_to_implementation_block = false
120
122
  end
121
123
 
@@ -142,13 +144,9 @@ module RSpec
142
144
  # allow(car).to receive(:go).and_raise(OutOfGas)
143
145
  # allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive")
144
146
  # allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz))
145
- def and_raise(exception=RuntimeError, message=nil)
147
+ def and_raise(*args)
146
148
  raise_already_invoked_error_if_necessary(__method__)
147
- if exception.respond_to?(:exception)
148
- exception = message ? exception.exception(message) : exception.exception
149
- end
150
-
151
- self.terminal_implementation_action = Proc.new { raise exception }
149
+ self.terminal_implementation_action = Proc.new { raise(*args) }
152
150
  nil
153
151
  end
154
152
 
@@ -177,6 +175,11 @@ module RSpec
177
175
  def and_yield(*args, &block)
178
176
  raise_already_invoked_error_if_necessary(__method__)
179
177
  yield @eval_context = Object.new if block
178
+
179
+ # Initialize args to yield now that it's being used, see also: comment
180
+ # in constructor.
181
+ @args_to_yield ||= []
182
+
180
183
  @args_to_yield << args
181
184
  self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
182
185
  self
@@ -248,7 +251,7 @@ module RSpec
248
251
  # @example
249
252
  # expect(car).to receive(:stop).never
250
253
  def never
251
- ErrorGenerator.raise_double_negation_error("expect(obj)") if negative?
254
+ error_generator.raise_double_negation_error("expect(obj)") if negative?
252
255
  @expected_received_count = 0
253
256
  self
254
257
  end
@@ -371,8 +374,10 @@ module RSpec
371
374
  @expectation_type = type
372
375
  @ordered = false
373
376
  @at_least = @at_most = @exactly = nil
374
- @args_to_yield = []
375
- @failed_fast = nil
377
+
378
+ # Initialized to nil so that we don't allocate an array for every
379
+ # mock or stub. See also comment in `and_yield`.
380
+ @args_to_yield = nil
376
381
  @eval_context = nil
377
382
  @yield_receiver_to_implementation_block = false
378
383
 
@@ -426,9 +431,8 @@ module RSpec
426
431
  end
427
432
 
428
433
  def verify_messages_received
429
- InsertOntoBacktrace.line(@expected_from) do
430
- generate_error unless expected_messages_received? || failed_fast?
431
- end
434
+ return if expected_messages_received?
435
+ generate_error
432
436
  end
433
437
 
434
438
  def expected_messages_received?
@@ -464,14 +468,28 @@ module RSpec
464
468
  similar_messages << args
465
469
  end
466
470
 
471
+ def unadvise(args)
472
+ similar_messages.delete_if { |message| args.include?(message) }
473
+ end
474
+
467
475
  def generate_error
468
476
  if similar_messages.empty?
469
- @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *expected_args)
477
+ @error_generator.raise_expectation_error(
478
+ @message, @expected_received_count, @argument_list_matcher,
479
+ @actual_received_count, expectation_count_type, expected_args,
480
+ @expected_from, exception_source_id
481
+ )
470
482
  else
471
- @error_generator.raise_similar_message_args_error(self, *@similar_messages)
483
+ @error_generator.raise_similar_message_args_error(
484
+ self, @similar_messages, @expected_from
485
+ )
472
486
  end
473
487
  end
474
488
 
489
+ def raise_unexpected_message_args_error(args_for_multiple_calls)
490
+ @error_generator.raise_unexpected_message_args_error(self, args_for_multiple_calls, exception_source_id)
491
+ end
492
+
475
493
  def expectation_count_type
476
494
  return :at_least if @at_least
477
495
  return :at_most if @at_most
@@ -481,7 +499,7 @@ module RSpec
481
499
  def description_for(verb)
482
500
  @error_generator.describe_expectation(
483
501
  verb, @message, @expected_received_count,
484
- @actual_received_count, *expected_args
502
+ @actual_received_count, expected_args
485
503
  )
486
504
  end
487
505
 
@@ -512,28 +530,33 @@ module RSpec
512
530
 
513
531
  private
514
532
 
533
+ def exception_source_id
534
+ @exception_source_id ||= "#{self.class.name} #{__id__}"
535
+ end
536
+
515
537
  def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block)
516
538
  args.unshift(orig_object) if yield_receiver_to_implementation_block?
517
539
 
518
540
  if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count))
519
- @actual_received_count += increment
520
- @failed_fast = true
521
541
  # args are the args we actually received, @argument_list_matcher is the
522
542
  # list of args we were expecting
523
- @error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args)
543
+ @error_generator.raise_expectation_error(
544
+ @message, @expected_received_count,
545
+ @argument_list_matcher,
546
+ @actual_received_count + increment,
547
+ expectation_count_type, args, nil, exception_source_id
548
+ )
524
549
  end
525
550
 
526
551
  @order_group.handle_order_constraint self
527
552
 
528
- begin
529
- if implementation.present?
530
- implementation.call(*args, &block)
531
- elsif parent_stub
532
- parent_stub.invoke(nil, *args, &block)
533
- end
534
- ensure
535
- @actual_received_count += increment
553
+ if implementation.present?
554
+ implementation.call(*args, &block)
555
+ elsif parent_stub
556
+ parent_stub.invoke(nil, *args, &block)
536
557
  end
558
+ ensure
559
+ @actual_received_count += increment
537
560
  end
538
561
 
539
562
  def has_been_invoked?
@@ -543,15 +566,7 @@ module RSpec
543
566
  def raise_already_invoked_error_if_necessary(calling_customization)
544
567
  return unless has_been_invoked?
545
568
 
546
- error_message = "The message expectation for #{orig_object.inspect}.#{message} has already been invoked " \
547
- "and cannot be modified further (e.g. using `#{calling_customization}`). All message expectation " \
548
- "customizations must be applied before it is used for the first time."
549
-
550
- raise MockExpectationAlreadyInvokedError, error_message
551
- end
552
-
553
- def failed_fast?
554
- @failed_fast
569
+ error_generator.raise_already_invoked_error(message, calling_customization)
555
570
  end
556
571
 
557
572
  def set_expected_received_count(relativity, n)
@@ -698,17 +713,5 @@ module RSpec
698
713
  "to call the original implementation, and cannot be modified further."
699
714
  end
700
715
  end
701
-
702
- # Insert original locations into stacktraces
703
- #
704
- # @private
705
- class InsertOntoBacktrace
706
- def self.line(location)
707
- yield
708
- rescue RSpec::Mocks::MockExpectationError => error
709
- error.backtrace.insert(0, location)
710
- Kernel.raise error
711
- end
712
- end
713
716
  end
714
717
  end
@@ -18,20 +18,24 @@ module RSpec
18
18
  @stubs = []
19
19
  end
20
20
 
21
- def original_method
21
+ def original_implementation_callable
22
22
  # If original method is not present, uses the `method_missing`
23
23
  # handler of the object. This accounts for cases where the user has not
24
24
  # correctly defined `respond_to?`, and also 1.8 which does not provide
25
25
  # method handles for missing methods even if `respond_to?` is correct.
26
- @original_method ||=
27
- @method_stasher.original_method ||
28
- @proxy.original_method_handle_for(method_name) ||
26
+ @original_implementation_callable ||= original_method ||
29
27
  Proc.new do |*args, &block|
30
28
  @object.__send__(:method_missing, @method_name, *args, &block)
31
29
  end
32
30
  end
33
31
 
34
- alias_method :save_original_method!, :original_method
32
+ alias_method :save_original_implementation_callable!, :original_implementation_callable
33
+
34
+ def original_method
35
+ @original_method ||=
36
+ @method_stasher.original_method ||
37
+ @proxy.original_method_handle_for(method_name)
38
+ end
35
39
 
36
40
  # @private
37
41
  def visibility
@@ -45,7 +49,7 @@ module RSpec
45
49
 
46
50
  # @private
47
51
  def configure_method
48
- @original_visibility = [visibility, method_name]
52
+ @original_visibility = visibility
49
53
  @method_stasher.stash unless @method_is_proxied
50
54
  define_proxy_method
51
55
  end
@@ -54,7 +58,7 @@ module RSpec
54
58
  def define_proxy_method
55
59
  return if @method_is_proxied
56
60
 
57
- save_original_method!
61
+ save_original_implementation_callable!
58
62
  definition_target.class_exec(self, method_name, visibility) do |method_double, method_name, visibility|
59
63
  define_method(method_name) do |*args, &block|
60
64
  method_double.proxy_method_invoked(self, *args, &block)
@@ -101,7 +105,7 @@ module RSpec
101
105
  return unless @original_visibility &&
102
106
  MethodReference.method_defined_at_any_visibility?(object_singleton_class, @method_name)
103
107
 
104
- object_singleton_class.__send__(*@original_visibility)
108
+ object_singleton_class.__send__(@original_visibility, method_name)
105
109
  end
106
110
 
107
111
  # @private
@@ -198,7 +202,7 @@ module RSpec
198
202
 
199
203
  # @private
200
204
  def raise_method_not_stubbed_error
201
- raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
205
+ RSpec::Mocks.error_generator.raise_method_not_stubbed_error(method_name)
202
206
  end
203
207
 
204
208
  # In Ruby 2.0.0 and above prepend will alter the method lookup chain.
@@ -6,6 +6,10 @@ module RSpec
6
6
  #
7
7
  # @private
8
8
  class MethodReference
9
+ def self.for(object_reference, method_name)
10
+ new(object_reference, method_name)
11
+ end
12
+
9
13
  def initialize(object_reference, method_name)
10
14
  @object_reference = object_reference
11
15
  @method_name = method_name
@@ -133,6 +137,14 @@ module RSpec
133
137
 
134
138
  # @private
135
139
  class ObjectMethodReference < MethodReference
140
+ def self.for(object_reference, method_name)
141
+ if ClassNewMethodReference.applies_to?(method_name) { object_reference.when_loaded { |o| o } }
142
+ ClassNewMethodReference.new(object_reference, method_name)
143
+ else
144
+ super
145
+ end
146
+ end
147
+
136
148
  private
137
149
 
138
150
  def method_implemented?(object)
@@ -151,5 +163,30 @@ module RSpec
151
163
  MethodReference.method_visibility_for(object, @method_name)
152
164
  end
153
165
  end
166
+
167
+ # When a class's `.new` method is stubbed, we want to use the method
168
+ # signature from `#initialize` because `.new`'s signature is a generic
169
+ # `def new(*args)` and it simply delegates to `#initialize` and forwards
170
+ # all args...so the method with the actually used signature is `#initialize`.
171
+ #
172
+ # This method reference implementation handles that specific case.
173
+ # @private
174
+ class ClassNewMethodReference < ObjectMethodReference
175
+ def self.applies_to?(method_name)
176
+ return false unless method_name == :new
177
+ klass = yield
178
+ return false unless klass.respond_to?(:new, true)
179
+
180
+ # We only want to apply our special logic to normal `new` methods.
181
+ # Methods that the user has monkeyed with should be left as-is.
182
+ klass.method(:new).owner == ::Class
183
+ end
184
+
185
+ def with_signature
186
+ @object_reference.when_loaded do |klass|
187
+ yield Support::MethodSignature.new(klass.instance_method(:initialize))
188
+ end
189
+ end
190
+ end
154
191
  end
155
192
  end
@@ -14,11 +14,10 @@ module RSpec
14
14
  end
15
15
 
16
16
  # @private
17
- def initialize(object, order_group, name=nil, options={})
17
+ def initialize(object, order_group, options={})
18
18
  @object = object
19
19
  @order_group = order_group
20
- @name = name
21
- @error_generator = ErrorGenerator.new(object, name)
20
+ @error_generator = ErrorGenerator.new(object)
22
21
  @messages_received = []
23
22
  @options = options
24
23
  @null_object = false
@@ -46,8 +45,10 @@ module RSpec
46
45
  nil
47
46
  end
48
47
 
48
+ DEFAULT_MESSAGE_EXPECTATION_OPTS = {}.freeze
49
+
49
50
  # @private
50
- def add_message_expectation(method_name, opts={}, &block)
51
+ def add_message_expectation(method_name, opts=DEFAULT_MESSAGE_EXPECTATION_OPTS, &block)
51
52
  location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line }
52
53
  meth_double = method_double_for(method_name)
53
54
 
@@ -98,11 +99,17 @@ module RSpec
98
99
 
99
100
  # @private
100
101
  def check_for_unexpected_arguments(expectation)
101
- @messages_received.each do |(method_name, args, _)|
102
- next unless expectation.matches_name_but_not_args(method_name, *args)
102
+ return if @messages_received.empty?
103
+
104
+ return if @messages_received.any? { |method_name, args, _| expectation.matches?(method_name, *args) }
103
105
 
104
- raise_unexpected_message_args_error(expectation, *args)
106
+ name_but_not_args, others = @messages_received.partition do |(method_name, args, _)|
107
+ expectation.matches_name_but_not_args(method_name, *args)
105
108
  end
109
+
110
+ return if name_but_not_args.empty? && !others.empty?
111
+
112
+ expectation.raise_unexpected_message_args_error(name_but_not_args.map { |args| args[1] })
106
113
  end
107
114
 
108
115
  # @private
@@ -141,6 +148,11 @@ module RSpec
141
148
  @messages_received.any? { |array| array == [method_name, args, block] }
142
149
  end
143
150
 
151
+ # @private
152
+ def messages_arg_list
153
+ @messages_received.map { |_, args, _| args }
154
+ end
155
+
144
156
  # @private
145
157
  def has_negative_expectation?(message)
146
158
  method_double_for(message).expectations.find { |expectation| expectation.negative_expectation_for?(message) }
@@ -166,16 +178,17 @@ module RSpec
166
178
  end
167
179
  stub.invoke(nil, *args, &block)
168
180
  elsif expectation
181
+ expectation.unadvise(messages_arg_list)
169
182
  expectation.invoke(stub, *args, &block)
170
183
  elsif (expectation = find_almost_matching_expectation(message, *args))
171
184
  expectation.advise(*args) if null_object? unless expectation.expected_messages_received?
172
185
 
173
186
  if null_object? || !has_negative_expectation?(message)
174
- raise_unexpected_message_args_error(expectation, *args)
187
+ expectation.raise_unexpected_message_args_error([args])
175
188
  end
176
189
  elsif (stub = find_almost_matching_stub(message, *args))
177
190
  stub.advise(*args)
178
- raise_missing_default_stub_error(stub, *args)
191
+ raise_missing_default_stub_error(stub, [args])
179
192
  elsif Class === @object
180
193
  @object.superclass.__send__(message, *args, &block)
181
194
  else
@@ -184,18 +197,13 @@ module RSpec
184
197
  end
185
198
 
186
199
  # @private
187
- def raise_unexpected_message_error(method_name, *args)
188
- @error_generator.raise_unexpected_message_error method_name, *args
189
- end
190
-
191
- # @private
192
- def raise_unexpected_message_args_error(expectation, *args)
193
- @error_generator.raise_unexpected_message_args_error(expectation, *args)
200
+ def raise_unexpected_message_error(method_name, args)
201
+ @error_generator.raise_unexpected_message_error method_name, args
194
202
  end
195
203
 
196
204
  # @private
197
- def raise_missing_default_stub_error(expectation, *args)
198
- @error_generator.raise_missing_default_stub_error(expectation, *args)
205
+ def raise_missing_default_stub_error(expectation, args_for_multiple_calls)
206
+ @error_generator.raise_missing_default_stub_error(expectation, args_for_multiple_calls)
199
207
  end
200
208
 
201
209
  # @private