rspec-mocks 3.0.0.beta2 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Changelog.md +104 -36
  5. data/README.md +35 -4
  6. data/lib/rspec/mocks.rb +52 -13
  7. data/lib/rspec/mocks/any_instance.rb +10 -0
  8. data/lib/rspec/mocks/any_instance/chain.rb +17 -0
  9. data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +1 -1
  10. data/lib/rspec/mocks/any_instance/expectation_chain.rb +9 -6
  11. data/lib/rspec/mocks/any_instance/message_chains.rb +10 -1
  12. data/lib/rspec/mocks/any_instance/proxy.rb +112 -0
  13. data/lib/rspec/mocks/any_instance/recorder.rb +49 -18
  14. data/lib/rspec/mocks/any_instance/stub_chain.rb +5 -3
  15. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +1 -1
  16. data/lib/rspec/mocks/argument_list_matcher.rb +5 -3
  17. data/lib/rspec/mocks/argument_matchers.rb +28 -12
  18. data/lib/rspec/mocks/configuration.rb +29 -0
  19. data/lib/rspec/mocks/error_generator.rb +2 -5
  20. data/lib/rspec/mocks/example_methods.rb +122 -13
  21. data/lib/rspec/mocks/instance_method_stasher.rb +26 -2
  22. data/lib/rspec/mocks/marshal_extension.rb +41 -0
  23. data/lib/rspec/mocks/matchers/expectation_customization.rb +20 -0
  24. data/lib/rspec/mocks/matchers/receive.rb +6 -20
  25. data/lib/rspec/mocks/matchers/receive_message_chain.rb +7 -5
  26. data/lib/rspec/mocks/matchers/receive_messages.rb +1 -2
  27. data/lib/rspec/mocks/message_chain.rb +9 -9
  28. data/lib/rspec/mocks/message_expectation.rb +6 -5
  29. data/lib/rspec/mocks/method_double.rb +67 -10
  30. data/lib/rspec/mocks/method_reference.rb +1 -1
  31. data/lib/rspec/mocks/mutate_const.rb +10 -14
  32. data/lib/rspec/mocks/object_reference.rb +7 -4
  33. data/lib/rspec/mocks/proxy.rb +95 -8
  34. data/lib/rspec/mocks/space.rb +58 -9
  35. data/lib/rspec/mocks/syntax.rb +128 -210
  36. data/lib/rspec/mocks/targets.rb +6 -8
  37. data/lib/rspec/mocks/test_double.rb +16 -25
  38. data/lib/rspec/mocks/verifying_double.rb +15 -12
  39. data/lib/rspec/mocks/verifying_message_expecation.rb +3 -3
  40. data/lib/rspec/mocks/verifying_proxy.rb +23 -17
  41. data/lib/rspec/mocks/version.rb +3 -1
  42. metadata +29 -257
  43. metadata.gz.sig +0 -0
  44. data/features/README.md +0 -75
  45. data/features/Scope.md +0 -17
  46. data/features/Upgrade.md +0 -22
  47. data/features/argument_matchers/README.md +0 -27
  48. data/features/argument_matchers/explicit.feature +0 -59
  49. data/features/argument_matchers/general_matchers.feature +0 -85
  50. data/features/argument_matchers/type_matchers.feature +0 -26
  51. data/features/message_expectations/README.md +0 -75
  52. data/features/message_expectations/allow_any_instance_of.feature +0 -26
  53. data/features/message_expectations/any_instance.feature +0 -43
  54. data/features/message_expectations/block_local_expectations.feature.pending +0 -55
  55. data/features/message_expectations/call_original.feature +0 -23
  56. data/features/message_expectations/expect_message_using_expect.feature +0 -107
  57. data/features/message_expectations/expect_message_using_should_receive.feature +0 -118
  58. data/features/message_expectations/message_chains_using_expect.feature +0 -49
  59. data/features/message_expectations/receive_counts.feature +0 -209
  60. data/features/message_expectations/warn_when_expectation_is_set_on_nil.feature +0 -50
  61. data/features/method_stubs/README.md +0 -77
  62. data/features/method_stubs/allow_any_instance_of.feature +0 -136
  63. data/features/method_stubs/as_null_object.feature +0 -40
  64. data/features/method_stubs/receive_message_chain.feature +0 -51
  65. data/features/method_stubs/simple_return_value_with_allow.feature +0 -44
  66. data/features/method_stubs/simple_return_value_with_stub.feature +0 -64
  67. data/features/method_stubs/stub_implementation.feature +0 -48
  68. data/features/method_stubs/to_ary.feature +0 -51
  69. data/features/mutating_constants/README.md +0 -82
  70. data/features/mutating_constants/hiding_defined_constant.feature +0 -64
  71. data/features/mutating_constants/stub_defined_constant.feature +0 -78
  72. data/features/mutating_constants/stub_undefined_constant.feature +0 -50
  73. data/features/outside_rspec/standalone.feature +0 -33
  74. data/features/spies/spy_partial_mock_method.feature +0 -34
  75. data/features/spies/spy_pure_mock_method.feature +0 -76
  76. data/features/spies/spy_unstubbed_method.feature +0 -18
  77. data/features/step_definitions/additional_cli_steps.rb +0 -11
  78. data/features/support/env.rb +0 -22
  79. data/features/support/rubinius.rb +0 -6
  80. data/features/test_frameworks/test_unit.feature +0 -54
  81. data/features/verifying_doubles/README.md +0 -17
  82. data/features/verifying_doubles/class_doubles.feature +0 -73
  83. data/features/verifying_doubles/dynamic_classes.feature +0 -71
  84. data/features/verifying_doubles/instance_doubles.feature +0 -103
  85. data/features/verifying_doubles/object_doubles.feature +0 -65
  86. data/features/verifying_doubles/partial_doubles.feature +0 -34
  87. data/lib/rspec/mocks/extensions/marshal.rb +0 -17
  88. data/lib/rspec/mocks/framework.rb +0 -35
  89. data/lib/rspec/mocks/method_signature_verifier.rb +0 -207
  90. data/lib/rspec/mocks/ruby_features.rb +0 -24
  91. data/spec/rspec/mocks/and_call_original_spec.rb +0 -268
  92. data/spec/rspec/mocks/and_return_spec.rb +0 -23
  93. data/spec/rspec/mocks/and_yield_spec.rb +0 -126
  94. data/spec/rspec/mocks/any_instance/message_chains_spec.rb +0 -41
  95. data/spec/rspec/mocks/any_instance_spec.rb +0 -1045
  96. data/spec/rspec/mocks/array_including_matcher_spec.rb +0 -41
  97. data/spec/rspec/mocks/at_least_spec.rb +0 -123
  98. data/spec/rspec/mocks/at_most_spec.rb +0 -90
  99. data/spec/rspec/mocks/before_all_spec.rb +0 -132
  100. data/spec/rspec/mocks/block_return_value_spec.rb +0 -70
  101. data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +0 -204
  102. data/spec/rspec/mocks/configuration_spec.rb +0 -242
  103. data/spec/rspec/mocks/double_spec.rb +0 -874
  104. data/spec/rspec/mocks/example_methods_spec.rb +0 -13
  105. data/spec/rspec/mocks/extensions/marshal_spec.rb +0 -54
  106. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +0 -184
  107. data/spec/rspec/mocks/hash_excluding_matcher_spec.rb +0 -67
  108. data/spec/rspec/mocks/hash_including_matcher_spec.rb +0 -94
  109. data/spec/rspec/mocks/instance_method_stasher_spec.rb +0 -74
  110. data/spec/rspec/mocks/matchers/have_received_spec.rb +0 -347
  111. data/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +0 -198
  112. data/spec/rspec/mocks/matchers/receive_messages_spec.rb +0 -140
  113. data/spec/rspec/mocks/matchers/receive_spec.rb +0 -418
  114. data/spec/rspec/mocks/method_signature_verifier_spec.rb +0 -272
  115. data/spec/rspec/mocks/methods_spec.rb +0 -26
  116. data/spec/rspec/mocks/mock_expectation_error_spec.rb +0 -22
  117. data/spec/rspec/mocks/mock_ordering_spec.rb +0 -114
  118. data/spec/rspec/mocks/multiple_return_value_spec.rb +0 -132
  119. data/spec/rspec/mocks/mutate_const_spec.rb +0 -542
  120. data/spec/rspec/mocks/nil_expectation_warning_spec.rb +0 -52
  121. data/spec/rspec/mocks/null_object_mock_spec.rb +0 -133
  122. data/spec/rspec/mocks/once_counts_spec.rb +0 -52
  123. data/spec/rspec/mocks/options_hash_spec.rb +0 -35
  124. data/spec/rspec/mocks/order_group_spec.rb +0 -27
  125. data/spec/rspec/mocks/partial_double_spec.rb +0 -308
  126. data/spec/rspec/mocks/partial_double_using_mocks_directly_spec.rb +0 -95
  127. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +0 -145
  128. data/spec/rspec/mocks/precise_counts_spec.rb +0 -68
  129. data/spec/rspec/mocks/record_messages_spec.rb +0 -26
  130. data/spec/rspec/mocks/serialization_spec.rb +0 -90
  131. data/spec/rspec/mocks/space_spec.rb +0 -244
  132. data/spec/rspec/mocks/stash_spec.rb +0 -46
  133. data/spec/rspec/mocks/stub_chain_spec.rb +0 -168
  134. data/spec/rspec/mocks/stub_implementation_spec.rb +0 -81
  135. data/spec/rspec/mocks/stub_spec.rb +0 -327
  136. data/spec/rspec/mocks/stubbed_message_expectations_spec.rb +0 -58
  137. data/spec/rspec/mocks/syntax_agnostic_message_matchers_spec.rb +0 -101
  138. data/spec/rspec/mocks/syntax_spec.rb +0 -19
  139. data/spec/rspec/mocks/test_double_spec.rb +0 -49
  140. data/spec/rspec/mocks/to_ary_spec.rb +0 -54
  141. data/spec/rspec/mocks/twice_counts_spec.rb +0 -66
  142. data/spec/rspec/mocks/verifying_double_spec.rb +0 -590
  143. data/spec/rspec/mocks/verifying_message_expecation_spec.rb +0 -69
  144. data/spec/rspec/mocks_spec.rb +0 -192
  145. data/spec/spec_helper.rb +0 -121
@@ -58,7 +58,7 @@ module RSpec
58
58
  end
59
59
 
60
60
  def any_instance_of(subject)
61
- ::RSpec::Mocks.space.any_instance_recorder_for(subject)
61
+ ::RSpec::Mocks.space.any_instance_proxy_for(subject)
62
62
  end
63
63
 
64
64
  def each_message_on(host)
@@ -66,7 +66,6 @@ module RSpec
66
66
  yield host, message, value
67
67
  end
68
68
  end
69
-
70
69
  end
71
70
  end
72
71
  end
@@ -20,17 +20,17 @@ module RSpec
20
20
  chain_on(matching_expectation.invoke_without_incrementing_received_count(nil), *chain, &@block)
21
21
  else
22
22
  next_in_chain = Double.new
23
- expectation(object, chain.shift, next_in_chain)
23
+ expectation(object, chain.shift) { next_in_chain }
24
24
  chain_on(next_in_chain, *chain, &@block)
25
25
  end
26
26
  else
27
- ::RSpec::Mocks.allow_message(object, chain.shift, {}, &block)
27
+ expectation(object, chain.shift, &@block)
28
28
  end
29
29
  end
30
30
 
31
31
  private
32
32
 
33
- def expectation(object, message, returned_object)
33
+ def expectation(object, message, &return_block)
34
34
  raise NotImplementedError.new
35
35
  end
36
36
 
@@ -68,10 +68,10 @@ module RSpec
68
68
  new(object, *chain, &blk).setup_chain
69
69
  end
70
70
 
71
- private
71
+ private
72
72
 
73
- def expectation(object, message, returned_object)
74
- ::RSpec::Mocks.expect_message(object, message, {}) { returned_object }
73
+ def expectation(object, message, &return_block)
74
+ ::RSpec::Mocks.expect_message(object, message, {}, &return_block)
75
75
  end
76
76
  end
77
77
 
@@ -81,10 +81,10 @@ module RSpec
81
81
  new(object, *chain, &blk).setup_chain
82
82
  end
83
83
 
84
- private
84
+ private
85
85
 
86
- def expectation(object, message, returned_object)
87
- ::RSpec::Mocks.allow_message(object, message, {}) { returned_object }
86
+ def expectation(object, message, &return_block)
87
+ ::RSpec::Mocks.allow_message(object, message, {}, &return_block)
88
88
  end
89
89
  end
90
90
  end
@@ -5,6 +5,8 @@ module RSpec
5
5
  # for a message. While this same effect can be achieved using a standard
6
6
  # MessageExpecation, this version is much faster and so can be used as an
7
7
  # optimization.
8
+ #
9
+ # @private
8
10
  class SimpleMessageExpectation
9
11
 
10
12
  def initialize(message, response, error_generator, backtrace_line = nil)
@@ -68,8 +70,6 @@ module RSpec
68
70
  self.inner_implementation_action = implementation_block
69
71
  end
70
72
 
71
- # @private
72
-
73
73
  # @private
74
74
  def expected_args
75
75
  @argument_list_matcher.expected_args
@@ -541,10 +541,10 @@ module RSpec
541
541
 
542
542
  @error_generator.raise_missing_block_error @args_to_yield unless block
543
543
  value = nil
544
- block_signature = BlockSignature.new(block)
544
+ block_signature = Support::BlockSignature.new(block)
545
545
 
546
546
  @args_to_yield.each do |args|
547
- unless MethodSignatureVerifier.new(block_signature, args).valid?
547
+ unless Support::MethodSignatureVerifier.new(block_signature, args).valid?
548
548
  @error_generator.raise_wrong_arity_error(args, block_signature)
549
549
  end
550
550
 
@@ -635,7 +635,8 @@ module RSpec
635
635
  end
636
636
 
637
637
  # Insert original locations into stacktraces
638
- # @api private
638
+ #
639
+ # @private
639
640
  class InsertOntoBacktrace
640
641
  def self.line(location)
641
642
  yield
@@ -25,7 +25,7 @@ module RSpec
25
25
  # method handles for missing methods even if `respond_to?` is correct.
26
26
  @original_method ||=
27
27
  @method_stasher.original_method ||
28
- @proxy.method_handle_for(method_name) ||
28
+ @proxy.original_method_handle_for(method_name) ||
29
29
  Proc.new do |*args, &block|
30
30
  @object.__send__(:method_missing, @method_name, *args, &block)
31
31
  end
@@ -55,8 +55,7 @@ module RSpec
55
55
  return if @method_is_proxied
56
56
 
57
57
  save_original_method!
58
-
59
- object_singleton_class.class_exec(self, method_name, visibility) do |method_double, method_name, visibility|
58
+ definition_target.class_exec(self, method_name, visibility) do |method_double, method_name, visibility|
60
59
  define_method(method_name) do |*args, &block|
61
60
  method_double.proxy_method_invoked(self, *args, &block)
62
61
  end
@@ -76,9 +75,11 @@ module RSpec
76
75
 
77
76
  # @private
78
77
  def restore_original_method
78
+ return show_frozen_warning if object_singleton_class.frozen?
79
79
  return unless @method_is_proxied
80
80
 
81
- object_singleton_class.__send__(:remove_method, @method_name)
81
+ definition_target.__send__(:remove_method, @method_name)
82
+
82
83
  if @method_stasher.method_is_stashed?
83
84
  @method_stasher.restore
84
85
  end
@@ -87,6 +88,15 @@ module RSpec
87
88
  @method_is_proxied = false
88
89
  end
89
90
 
91
+ # @private
92
+ def show_frozen_warning
93
+ RSpec.warn_with(
94
+ "WARNING: rspec-mocks was unable to restore the original `#{@method_name}` method on #{@object.inspect} because it has been frozen. If you reuse this object, `#{@method_name}` will continue to respond with its stub implementation.",
95
+ :call_site => nil,
96
+ :use_spec_location_as_call_site => true
97
+ )
98
+ end
99
+
90
100
  # @private
91
101
  def restore_original_visibility
92
102
  return unless @original_visibility &&
@@ -179,13 +189,12 @@ module RSpec
179
189
  # @private
180
190
  def remove_stub
181
191
  raise_method_not_stubbed_error if stubs.empty?
182
- expectations.empty? ? reset : stubs.clear
192
+ remove_stub_if_present
183
193
  end
184
194
 
185
195
  # @private
186
- def remove_single_stub(stub)
187
- stubs.delete(stub)
188
- restore_original_method if stubs.empty? && expectations.empty?
196
+ def remove_stub_if_present
197
+ expectations.empty? ? reset : stubs.clear
189
198
  end
190
199
 
191
200
  # @private
@@ -193,8 +202,56 @@ module RSpec
193
202
  raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
194
203
  end
195
204
 
196
- # @private
197
- IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored'
205
+ private
206
+
207
+ # In Ruby 2.0.0 and above prepend will alter the method lookup chain.
208
+ # We use an object's singleton class to define method doubles upon,
209
+ # however if the object has had it's singleton class (as opposed to
210
+ # it's actual class) prepended too then the the method lookup chain
211
+ # will look in the prepended module first, **before** the singleton
212
+ # class.
213
+ #
214
+ # This code works around that by providing a mock definition target
215
+ # that is either the singleton class, or if necessary, a prepended module
216
+ # of our own.
217
+ #
218
+ if Support::RubyFeatures.module_prepends_supported?
219
+ # We subclass `Module` in order to be able to easily detect our prepended module.
220
+ RSpecPrependedModule = Class.new(Module)
221
+
222
+ def definition_target
223
+ @definition_target ||= usable_rspec_prepended_module || object_singleton_class
224
+ end
225
+
226
+ def usable_rspec_prepended_module
227
+ @proxy.prepended_modules_of_singleton_class.each do |mod|
228
+ # If we have one of our modules prepended before one of the user's
229
+ # modules that defines the method, use that, since our module's
230
+ # definition will take precedence.
231
+ return mod if RSpecPrependedModule === mod
232
+
233
+ # If we hit a user module with the method defined first,
234
+ # we must create a new prepend module, even if one exists later,
235
+ # because ours will only take precedence if it comes first.
236
+ return new_rspec_prepended_module if mod.method_defined?(method_name)
237
+ end
238
+
239
+ nil
240
+ end
241
+
242
+ def new_rspec_prepended_module
243
+ RSpecPrependedModule.new.tap do |mod|
244
+ object_singleton_class.__send__ :prepend, mod
245
+ end
246
+ end
247
+
248
+ else
249
+
250
+ def definition_target
251
+ object_singleton_class
252
+ end
253
+
254
+ end
198
255
  end
199
256
  end
200
257
  end
@@ -45,7 +45,7 @@ module RSpec
45
45
 
46
46
  def with_signature
47
47
  if original = original_method
48
- yield MethodSignature.new(original)
48
+ yield Support::MethodSignature.new(original)
49
49
  end
50
50
  end
51
51
 
@@ -2,7 +2,8 @@ module RSpec
2
2
  module Mocks
3
3
  # Provides recursive constant lookup methods useful for
4
4
  # constant stubbing.
5
- # @api private
5
+ #
6
+ # @private
6
7
  module RecursiveConstMethods
7
8
  # We only want to consider constants that are defined directly on a
8
9
  # particular module, and not include top-level/inherited constants.
@@ -96,7 +97,7 @@ module RSpec
96
97
  # nil if the constant was not previously defined.
97
98
  attr_accessor :original_value
98
99
 
99
- # @api private
100
+ # @private
100
101
  attr_writer :previously_defined, :stubbed, :hidden
101
102
 
102
103
  # @return [Boolean] Whether or not the constant was defined
@@ -123,12 +124,13 @@ module RSpec
123
124
  @hidden
124
125
  end
125
126
 
127
+ # The default `to_s` isn't very useful, so a custom version is provided.
126
128
  def to_s
127
129
  "#<#{self.class.name} #{name}>"
128
130
  end
129
131
  alias inspect to_s
130
132
 
131
- # @api private
133
+ # @private
132
134
  def self.unmutated(name)
133
135
  const = new(name)
134
136
  const.previously_defined = recursive_const_defined?(name)
@@ -195,7 +197,7 @@ module RSpec
195
197
 
196
198
  # Contains common functionality used by all of the constant mutators.
197
199
  #
198
- # @api private
200
+ # @private
199
201
  class BaseMutator
200
202
  include RecursiveConstMethods
201
203
 
@@ -225,7 +227,7 @@ module RSpec
225
227
 
226
228
  # Hides a defined constant for the duration of an example.
227
229
  #
228
- # @api private
230
+ # @private
229
231
  class ConstantHider < BaseMutator
230
232
  def mutate
231
233
  return unless @defined = recursive_const_defined?(full_constant_name)
@@ -253,7 +255,7 @@ module RSpec
253
255
 
254
256
  # Replaces a defined constant for the duration of an example.
255
257
  #
256
- # @api private
258
+ # @private
257
259
  class DefinedConstantReplacer < BaseMutator
258
260
  def mutate
259
261
  @context = recursive_const_get(@context_parts.join('::'))
@@ -319,7 +321,7 @@ module RSpec
319
321
 
320
322
  # Sets an undefined constant for the duration of an example.
321
323
  #
322
- # @api private
324
+ # @private
323
325
  class UndefinedConstantSetter < BaseMutator
324
326
  def mutate
325
327
  @parent = @context_parts.inject(Object) do |klass, name|
@@ -361,7 +363,7 @@ module RSpec
361
363
  # the mutator is correctly registered so it can be backed out at the end
362
364
  # of the test.
363
365
  #
364
- # @api private
366
+ # @private
365
367
  def self.mutate(mutator)
366
368
  ::RSpec::Mocks.space.register_constant_mutator(mutator)
367
369
  mutator.mutate
@@ -380,11 +382,5 @@ module RSpec
380
382
  end
381
383
  end
382
384
  end
383
-
384
- # Keeps backwards compatibility since we had released an rspec-mocks that
385
- # only supported stubbing. Later, we released the hide_const feature and
386
- # decided that the term "mutator" was a better term to wrap up the concept
387
- # of both stubbing and hiding.
388
- ConstantStubber = ConstantMutator
389
385
  end
390
386
  end
@@ -1,7 +1,7 @@
1
1
  module RSpec
2
2
  module Mocks
3
3
 
4
- # @api private
4
+ # @private
5
5
  class ObjectReference
6
6
  # Returns an appropriate Object or Module reference based
7
7
  # on the given argument.
@@ -22,7 +22,8 @@ module RSpec
22
22
 
23
23
  # Used when an object is passed to `object_double`.
24
24
  # Represents a reference to that object.
25
- # @api private
25
+ #
26
+ # @private
26
27
  class DirectObjectReference
27
28
  def initialize(object)
28
29
  @object = object
@@ -48,7 +49,8 @@ module RSpec
48
49
 
49
50
  # Used when a module is passed to `class_double` or `instance_double`.
50
51
  # Represents a reference to that module.
51
- # @api private
52
+ #
53
+ # @private
52
54
  class DirectModuleReference < DirectObjectReference
53
55
  def const_to_replace
54
56
  @object.name
@@ -60,7 +62,8 @@ module RSpec
60
62
  # or `object_double`.
61
63
  # Represents a reference to the object named (via a constant lookup)
62
64
  # by the string.
63
- # @api private
65
+ #
66
+ # @private
64
67
  class NamedObjectReference
65
68
  def initialize(const_name)
66
69
  @const_name = const_name
@@ -37,12 +37,13 @@ module RSpec
37
37
  end
38
38
 
39
39
  # @private
40
- def method_handle_for(message)
40
+ def original_method_handle_for(message)
41
41
  nil
42
42
  end
43
43
 
44
44
  # @private
45
- def add_message_expectation(location, method_name, opts={}, &block)
45
+ def add_message_expectation(method_name, opts={}, &block)
46
+ location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line }
46
47
  meth_double = method_double_for(method_name)
47
48
 
48
49
  if null_object? && !block
@@ -101,7 +102,8 @@ module RSpec
101
102
  end
102
103
 
103
104
  # @private
104
- def add_stub(location, method_name, opts={}, &implementation)
105
+ def add_stub(method_name, opts={}, &implementation)
106
+ location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line }
105
107
  method_double_for(method_name).add_stub @error_generator, @order_group, location, opts, &implementation
106
108
  end
107
109
 
@@ -116,8 +118,8 @@ module RSpec
116
118
  end
117
119
 
118
120
  # @private
119
- def remove_single_stub(method_name, stub)
120
- method_double_for(method_name).remove_single_stub(stub)
121
+ def remove_stub_if_present(method_name)
122
+ method_double_for(method_name).remove_stub_if_present
121
123
  end
122
124
 
123
125
  # @private
@@ -149,6 +151,11 @@ module RSpec
149
151
  # @private
150
152
  def message_received(message, *args, &block)
151
153
  record_message_received message, *args, &block
154
+
155
+ RSpec::Mocks.space.any_instance_recorders_from_ancestry_of(object).each do |subscriber|
156
+ subscriber.notify_received_message(object, message, args, block)
157
+ end
158
+
152
159
  expectation = find_matching_expectation(message, *args)
153
160
  stub = find_matching_method_stub(message, *args)
154
161
 
@@ -194,6 +201,17 @@ module RSpec
194
201
  :public
195
202
  end
196
203
 
204
+ if Support::RubyFeatures.module_prepends_supported?
205
+ def prepended_modules_of_singleton_class
206
+ @prepended_modules_of_singleton_class ||= begin
207
+ singleton_class = @object.singleton_class
208
+ singleton_class.ancestors.take_while do |mod|
209
+ !(Class === mod)
210
+ end
211
+ end
212
+ end
213
+ end
214
+
197
215
  private
198
216
 
199
217
  def method_double_for(message)
@@ -244,7 +262,7 @@ module RSpec
244
262
 
245
263
  # @private
246
264
  class PartialDoubleProxy < Proxy
247
- def method_handle_for(message)
265
+ def original_method_handle_for(message)
248
266
  if any_instance_class_recorder_observing_method?(@object.class, message)
249
267
  message = ::RSpec::Mocks.space.
250
268
  any_instance_recorder_for(@object.class).
@@ -290,6 +308,75 @@ module RSpec
290
308
  end
291
309
  end
292
310
 
311
+ # @private
312
+ # When we mock or stub a method on a class, we have to treat it a bit different,
313
+ # because normally singleton method definitions only affect the object on which
314
+ # they are defined, but on classes they affect subclasses, too. As a result,
315
+ # we need some special handling to get the original method.
316
+ module PartialClassDoubleProxyMethods
317
+ def initialize(source_space, *args)
318
+ @source_space = source_space
319
+ super(*args)
320
+ end
321
+
322
+ # Consider this situation:
323
+ #
324
+ # class A; end
325
+ # class B < A; end
326
+ #
327
+ # allow(A).to receive(:new)
328
+ # expect(B).to receive(:new).and_call_original
329
+ #
330
+ # When getting the original definition for `B.new`, we cannot rely purely on
331
+ # using `B.method(:new)` before our redefinition is defined on `B`, because
332
+ # `B.method(:new)` will return a method that will execute the stubbed version
333
+ # of the method on `A` since singleton methods on classes are in the lookup
334
+ # hierarchy.
335
+ #
336
+ # To do it properly, we need to find the original definition of `new` from `A`
337
+ # from _before_ `A` was stubbed, and we need to rebind it to `B` so that it will
338
+ # run with the proper `self`.
339
+ #
340
+ # That's what this method (together with `original_unbound_method_handle_from_ancestor_for`)
341
+ # does.
342
+ def original_method_handle_for(message)
343
+ unbound_method = superclass_proxy &&
344
+ superclass_proxy.original_unbound_method_handle_from_ancestor_for(message.to_sym)
345
+
346
+ return super unless unbound_method
347
+ unbound_method.bind(object)
348
+ end
349
+
350
+ protected
351
+
352
+ def original_unbound_method_handle_from_ancestor_for(message)
353
+ method_double = @method_doubles.fetch(message) do
354
+ # The fact that there is no method double for this message indicates
355
+ # that it has not been redefined by rspec-mocks. We need to continue
356
+ # looking up the ancestor chain.
357
+ return superclass_proxy &&
358
+ superclass_proxy.original_unbound_method_handle_from_ancestor_for(message)
359
+ end
360
+
361
+ method_double.original_method.unbind
362
+ end
363
+
364
+ def superclass_proxy
365
+ return @superclass_proxy if defined?(@superclass_proxy)
366
+
367
+ if (superclass = object.superclass)
368
+ @superclass_proxy = @source_space.proxy_for(superclass)
369
+ else
370
+ @superclass_proxy = nil
371
+ end
372
+ end
373
+ end
374
+
375
+ # @private
376
+ class PartialClassDoubleProxy < PartialDoubleProxy
377
+ include PartialClassDoubleProxyMethods
378
+ end
379
+
293
380
  # @private
294
381
  class ProxyForNil < PartialDoubleProxy
295
382
  def initialize(order_group)
@@ -300,7 +387,7 @@ module RSpec
300
387
  attr_accessor :warn_about_expectations
301
388
  alias warn_about_expectations? warn_about_expectations
302
389
 
303
- def add_message_expectation(location, method_name, opts={}, &block)
390
+ def add_message_expectation(method_name, opts={}, &block)
304
391
  warn(method_name) if warn_about_expectations?
305
392
  super
306
393
  end
@@ -310,7 +397,7 @@ module RSpec
310
397
  super
311
398
  end
312
399
 
313
- def add_stub(location, method_name, opts={}, &implementation)
400
+ def add_stub(method_name, opts={}, &implementation)
314
401
  warn(method_name) if warn_about_expectations?
315
402
  super
316
403
  end