rspec-mocks 3.2.1 → 3.3.0

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