rspec-mocks 3.0.0.beta1 → 3.0.0.beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. data.tar.gz.sig +1 -1
  2. data/Changelog.md +95 -3
  3. data/README.md +27 -13
  4. data/features/README.md +15 -7
  5. data/features/argument_matchers/README.md +5 -5
  6. data/features/argument_matchers/explicit.feature +6 -6
  7. data/features/argument_matchers/general_matchers.feature +4 -4
  8. data/features/argument_matchers/type_matchers.feature +2 -2
  9. data/features/message_expectations/README.md +29 -27
  10. data/features/message_expectations/call_original.feature +0 -1
  11. data/features/message_expectations/message_chains_using_expect.feature +49 -0
  12. data/features/method_stubs/README.md +2 -1
  13. data/features/method_stubs/{any_instance.feature → allow_any_instance_of.feature} +12 -12
  14. data/features/method_stubs/{stub_chain.feature → receive_message_chain.feature} +3 -3
  15. data/features/method_stubs/to_ary.feature +1 -1
  16. data/features/mutating_constants/stub_defined_constant.feature +0 -1
  17. data/features/outside_rspec/standalone.feature +1 -1
  18. data/features/spies/spy_pure_mock_method.feature +1 -1
  19. data/features/test_frameworks/test_unit.feature +21 -10
  20. data/features/verifying_doubles/README.md +17 -0
  21. data/features/verifying_doubles/class_doubles.feature +1 -16
  22. data/features/verifying_doubles/dynamic_classes.feature +0 -1
  23. data/features/verifying_doubles/{introduction.feature → instance_doubles.feature} +41 -23
  24. data/features/verifying_doubles/partial_doubles.feature +2 -2
  25. data/lib/rspec/mocks.rb +69 -82
  26. data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +35 -0
  27. data/lib/rspec/mocks/any_instance/expectation_chain.rb +1 -2
  28. data/lib/rspec/mocks/any_instance/recorder.rb +52 -18
  29. data/lib/rspec/mocks/any_instance/stub_chain.rb +1 -1
  30. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +4 -0
  31. data/lib/rspec/mocks/argument_list_matcher.rb +10 -44
  32. data/lib/rspec/mocks/argument_matchers.rb +132 -163
  33. data/lib/rspec/mocks/configuration.rb +28 -4
  34. data/lib/rspec/mocks/error_generator.rb +46 -13
  35. data/lib/rspec/mocks/example_methods.rb +13 -12
  36. data/lib/rspec/mocks/extensions/marshal.rb +1 -1
  37. data/lib/rspec/mocks/framework.rb +3 -4
  38. data/lib/rspec/mocks/instance_method_stasher.rb +2 -3
  39. data/lib/rspec/mocks/matchers/have_received.rb +8 -6
  40. data/lib/rspec/mocks/matchers/receive.rb +28 -20
  41. data/lib/rspec/mocks/matchers/receive_message_chain.rb +65 -0
  42. data/lib/rspec/mocks/matchers/receive_messages.rb +3 -2
  43. data/lib/rspec/mocks/message_chain.rb +91 -0
  44. data/lib/rspec/mocks/message_expectation.rb +86 -80
  45. data/lib/rspec/mocks/method_double.rb +2 -11
  46. data/lib/rspec/mocks/method_reference.rb +82 -23
  47. data/lib/rspec/mocks/method_signature_verifier.rb +207 -0
  48. data/lib/rspec/mocks/mutate_const.rb +34 -50
  49. data/lib/rspec/mocks/object_reference.rb +0 -1
  50. data/lib/rspec/mocks/proxy.rb +70 -13
  51. data/lib/rspec/mocks/ruby_features.rb +24 -0
  52. data/lib/rspec/mocks/space.rb +105 -31
  53. data/lib/rspec/mocks/standalone.rb +2 -2
  54. data/lib/rspec/mocks/syntax.rb +43 -8
  55. data/lib/rspec/mocks/targets.rb +16 -7
  56. data/lib/rspec/mocks/test_double.rb +41 -15
  57. data/lib/rspec/mocks/verifying_double.rb +51 -4
  58. data/lib/rspec/mocks/verifying_message_expecation.rb +12 -12
  59. data/lib/rspec/mocks/verifying_proxy.rb +32 -19
  60. data/lib/rspec/mocks/version.rb +1 -1
  61. data/spec/rspec/mocks/and_call_original_spec.rb +28 -7
  62. data/spec/rspec/mocks/and_return_spec.rb +23 -0
  63. data/spec/rspec/mocks/and_yield_spec.rb +1 -2
  64. data/spec/rspec/mocks/any_instance_spec.rb +33 -17
  65. data/spec/rspec/mocks/array_including_matcher_spec.rb +6 -6
  66. data/spec/rspec/mocks/before_all_spec.rb +132 -0
  67. data/spec/rspec/mocks/block_return_value_spec.rb +12 -1
  68. data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +9 -11
  69. data/spec/rspec/mocks/configuration_spec.rb +14 -1
  70. data/spec/rspec/mocks/double_spec.rb +867 -24
  71. data/spec/rspec/mocks/example_methods_spec.rb +13 -0
  72. data/spec/rspec/mocks/extensions/marshal_spec.rb +17 -17
  73. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +29 -1
  74. data/spec/rspec/mocks/hash_excluding_matcher_spec.rb +12 -12
  75. data/spec/rspec/mocks/hash_including_matcher_spec.rb +21 -17
  76. data/spec/rspec/mocks/instance_method_stasher_spec.rb +2 -3
  77. data/spec/rspec/mocks/matchers/have_received_spec.rb +7 -0
  78. data/spec/rspec/mocks/matchers/receive_message_chain_spec.rb +198 -0
  79. data/spec/rspec/mocks/matchers/receive_messages_spec.rb +2 -2
  80. data/spec/rspec/mocks/matchers/receive_spec.rb +19 -6
  81. data/spec/rspec/mocks/method_signature_verifier_spec.rb +272 -0
  82. data/spec/rspec/mocks/methods_spec.rb +0 -1
  83. data/spec/rspec/mocks/multiple_return_value_spec.rb +2 -2
  84. data/spec/rspec/mocks/mutate_const_spec.rb +24 -1
  85. data/spec/rspec/mocks/nil_expectation_warning_spec.rb +6 -22
  86. data/spec/rspec/mocks/null_object_mock_spec.rb +13 -7
  87. data/spec/rspec/mocks/options_hash_spec.rb +3 -3
  88. data/spec/rspec/mocks/{partial_mock_spec.rb → partial_double_spec.rb} +5 -2
  89. data/spec/rspec/mocks/{partial_mock_using_mocks_directly_spec.rb → partial_double_using_mocks_directly_spec.rb} +1 -1
  90. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +18 -0
  91. data/spec/rspec/mocks/serialization_spec.rb +1 -0
  92. data/spec/rspec/mocks/space_spec.rb +218 -7
  93. data/spec/rspec/mocks/stub_chain_spec.rb +6 -0
  94. data/spec/rspec/mocks/stub_spec.rb +0 -6
  95. data/spec/rspec/mocks/syntax_spec.rb +19 -0
  96. data/spec/rspec/mocks/test_double_spec.rb +0 -1
  97. data/spec/rspec/mocks/verifying_double_spec.rb +281 -18
  98. data/spec/rspec/mocks/verifying_message_expecation_spec.rb +7 -6
  99. data/spec/rspec/mocks_spec.rb +168 -42
  100. data/spec/spec_helper.rb +34 -22
  101. metadata +94 -63
  102. metadata.gz.sig +0 -0
  103. checksums.yaml +0 -15
  104. checksums.yaml.gz.sig +0 -2
  105. data/features/outside_rspec/configuration.feature +0 -60
  106. data/lib/rspec/mocks/arity_calculator.rb +0 -66
  107. data/lib/rspec/mocks/errors.rb +0 -12
  108. data/lib/rspec/mocks/mock.rb +0 -7
  109. data/lib/rspec/mocks/proxy_for_nil.rb +0 -37
  110. data/lib/rspec/mocks/stub_chain.rb +0 -51
  111. data/spec/rspec/mocks/argument_expectation_spec.rb +0 -32
  112. data/spec/rspec/mocks/arity_calculator_spec.rb +0 -95
  113. data/spec/rspec/mocks/mock_space_spec.rb +0 -113
  114. data/spec/rspec/mocks/mock_spec.rb +0 -788
@@ -87,4 +87,3 @@ module RSpec
87
87
  end
88
88
  end
89
89
  end
90
-
@@ -14,7 +14,6 @@ module RSpec
14
14
  @order_group = order_group
15
15
  @name = name
16
16
  @error_generator = ErrorGenerator.new(object, name)
17
- @expectation_ordering = RSpec::Mocks::space.expectation_ordering
18
17
  @messages_received = []
19
18
  @options = options
20
19
  @null_object = false
@@ -47,12 +46,12 @@ module RSpec
47
46
  meth_double = method_double_for(method_name)
48
47
 
49
48
  if null_object? && !block
50
- meth_double.add_default_stub(@error_generator, @expectation_ordering, location, opts) do
49
+ meth_double.add_default_stub(@error_generator, @order_group, location, opts) do
51
50
  @object
52
51
  end
53
52
  end
54
53
 
55
- meth_double.add_expectation @error_generator, @expectation_ordering, location, opts, &block
54
+ meth_double.add_expectation @error_generator, @order_group, location, opts, &block
56
55
  end
57
56
 
58
57
  # @private
@@ -66,7 +65,7 @@ module RSpec
66
65
 
67
66
  meth_double.build_expectation(
68
67
  @error_generator,
69
- @expectation_ordering
68
+ @order_group
70
69
  )
71
70
  end
72
71
 
@@ -103,7 +102,7 @@ module RSpec
103
102
 
104
103
  # @private
105
104
  def add_stub(location, method_name, opts={}, &implementation)
106
- method_double_for(method_name).add_stub @error_generator, @expectation_ordering, location, opts, &implementation
105
+ method_double_for(method_name).add_stub @error_generator, @order_group, location, opts, &implementation
107
106
  end
108
107
 
109
108
  # @private
@@ -124,13 +123,10 @@ module RSpec
124
123
  # @private
125
124
  def verify
126
125
  @method_doubles.each_value {|d| d.verify}
127
- ensure
128
- reset
129
126
  end
130
127
 
131
128
  # @private
132
129
  def reset
133
- @method_doubles.each_value {|d| d.reset}
134
130
  @messages_received.clear
135
131
  end
136
132
 
@@ -192,7 +188,13 @@ module RSpec
192
188
  @error_generator.raise_missing_default_stub_error(expectation, *args)
193
189
  end
194
190
 
195
- private
191
+ # @private
192
+ def visibility_for(method_name)
193
+ # This is the default (for test doubles). Subclasses override this.
194
+ :public
195
+ end
196
+
197
+ private
196
198
 
197
199
  def method_double_for(message)
198
200
  @method_doubles[message.to_sym]
@@ -231,15 +233,25 @@ module RSpec
231
233
  end
232
234
  end
233
235
 
234
- class PartialMockProxy < Proxy
236
+ # @private
237
+ class TestDoubleProxy < Proxy
238
+ def reset
239
+ @method_doubles.clear
240
+ object.__disallow_further_usage!
241
+ super
242
+ end
243
+ end
244
+
245
+ # @private
246
+ class PartialDoubleProxy < Proxy
235
247
  def method_handle_for(message)
236
248
  if any_instance_class_recorder_observing_method?(@object.class, message)
237
- message = ::RSpec::Mocks.
249
+ message = ::RSpec::Mocks.space.
238
250
  any_instance_recorder_for(@object.class).
239
251
  build_alias_method_name(message)
240
252
  end
241
253
 
242
- ::RSpec::Mocks.method_handle_for(@object, message)
254
+ ::RSpec::Support.method_handle_for(@object, message)
243
255
  rescue NameError
244
256
  nil
245
257
  end
@@ -256,14 +268,59 @@ module RSpec
256
268
  super
257
269
  end
258
270
 
271
+ # @private
272
+ def visibility_for(method_name)
273
+ # We fall back to :public because by default we allow undefined methods
274
+ # to be stubbed, and when we do so, we make them public.
275
+ MethodReference.method_visibility_for(@object, method_name) || :public
276
+ end
277
+
278
+ def reset
279
+ @method_doubles.each_value {|d| d.reset}
280
+ super
281
+ end
282
+
259
283
  private
260
284
 
261
285
  def any_instance_class_recorder_observing_method?(klass, method_name)
262
- return true if ::RSpec::Mocks.any_instance_recorder_for(klass).already_observing?(method_name)
286
+ return true if ::RSpec::Mocks.space.any_instance_recorder_for(klass).already_observing?(method_name)
263
287
  superklass = klass.superclass
264
288
  return false if superklass.nil?
265
289
  any_instance_class_recorder_observing_method?(superklass, method_name)
266
290
  end
267
291
  end
292
+
293
+ # @private
294
+ class ProxyForNil < PartialDoubleProxy
295
+ def initialize(order_group)
296
+ @warn_about_expectations = true
297
+ super(nil, order_group)
298
+ end
299
+
300
+ attr_accessor :warn_about_expectations
301
+ alias warn_about_expectations? warn_about_expectations
302
+
303
+ def add_message_expectation(location, method_name, opts={}, &block)
304
+ warn(method_name) if warn_about_expectations?
305
+ super
306
+ end
307
+
308
+ def add_negative_message_expectation(location, method_name, &implementation)
309
+ warn(method_name) if warn_about_expectations?
310
+ super
311
+ end
312
+
313
+ def add_stub(location, method_name, opts={}, &implementation)
314
+ warn(method_name) if warn_about_expectations?
315
+ super
316
+ end
317
+
318
+ private
319
+
320
+ def warn method_name
321
+ source = CallerFilter.first_non_rspec_line
322
+ Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{source}. Use allow_message_expectations_on_nil to disable warnings.")
323
+ end
324
+ end
268
325
  end
269
326
  end
@@ -0,0 +1,24 @@
1
+ module RSpec
2
+ module Mocks
3
+ # @api private
4
+ #
5
+ # Provides query methods for ruby features that differ among
6
+ # implementations.
7
+ module RubyFeatures
8
+ def optional_and_splat_args_supported?
9
+ Method.method_defined?(:parameters)
10
+ end
11
+ module_function :optional_and_splat_args_supported?
12
+
13
+ def kw_args_supported?
14
+ RUBY_VERSION >= '2.0.0' && RUBY_ENGINE != 'rbx'
15
+ end
16
+ module_function :kw_args_supported?
17
+
18
+ def required_kw_args_supported?
19
+ RUBY_VERSION >= '2.1.0' && RUBY_ENGINE != 'rbx'
20
+ end
21
+ module_function :required_kw_args_supported?
22
+ end
23
+ end
24
+ end
@@ -1,44 +1,81 @@
1
1
  module RSpec
2
2
  module Mocks
3
+ # @private
4
+ # Provides a default space implementation for outside
5
+ # the scope of an example. Called "root" because it serves
6
+ # as the root of the space stack.
7
+ class RootSpace
8
+ def proxy_for(*args)
9
+ raise_lifecycle_message
10
+ end
11
+
12
+ def any_instance_recorder_for(*args)
13
+ raise_lifecycle_message
14
+ end
15
+
16
+ def register_constant_mutator(mutator)
17
+ raise_lifecycle_message
18
+ end
19
+
20
+ def reset_all
21
+ end
22
+
23
+ def verify_all
24
+ end
25
+
26
+ def registered?(object)
27
+ false
28
+ end
29
+
30
+ def new_scope
31
+ Space.new
32
+ end
33
+
34
+ private
35
+
36
+ def raise_lifecycle_message
37
+ raise OutsideOfExampleError,
38
+ "The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported."
39
+ end
40
+ end
41
+
3
42
  # @api private
4
43
  class Space
5
- attr_reader :proxies, :any_instance_recorders
44
+ attr_reader :proxies, :any_instance_recorders, :expectation_ordering
6
45
 
7
46
  def initialize
8
47
  @proxies = {}
9
48
  @any_instance_recorders = {}
49
+ @constant_mutators = []
50
+ @expectation_ordering = OrderGroup.new
10
51
  end
11
52
 
12
- def verify_all
13
- proxies.each_value do |object|
14
- object.verify
15
- end
53
+ def new_scope
54
+ NestedSpace.new(self)
55
+ end
16
56
 
17
- any_instance_recorders.each_value do |recorder|
18
- recorder.verify
19
- end
57
+ def verify_all
58
+ proxies.each_value { |proxy| proxy.verify }
59
+ any_instance_recorders.each_value { |recorder| recorder.verify }
20
60
  end
21
61
 
22
62
  def reset_all
23
- ConstantMutator.reset_all
24
-
25
- proxies.each_value do |object|
26
- object.reset
27
- end
63
+ proxies.each_value { |proxy| proxy.reset }
64
+ @constant_mutators.reverse.each { |mut| mut.idempotently_reset }
65
+ end
28
66
 
29
- proxies.clear
30
- any_instance_recorders.clear
31
- expectation_ordering.clear
67
+ def register_constant_mutator(mutator)
68
+ @constant_mutators << mutator
32
69
  end
33
70
 
34
- def expectation_ordering
35
- @expectation_ordering ||= OrderGroup.new
71
+ def constant_mutator_for(name)
72
+ @constant_mutators.find { |m| m.full_constant_name == name }
36
73
  end
37
74
 
38
75
  def any_instance_recorder_for(klass)
39
76
  id = klass.__id__
40
77
  any_instance_recorders.fetch(id) do
41
- any_instance_recorders[id] = AnyInstance::Recorder.new(klass)
78
+ any_instance_recorder_not_found_for(id, klass)
42
79
  end
43
80
  end
44
81
 
@@ -52,18 +89,7 @@ module RSpec
52
89
 
53
90
  def proxy_for(object)
54
91
  id = id_for(object)
55
- proxies.fetch(id) do
56
- proxies[id] = case object
57
- when NilClass then ProxyForNil.new(expectation_ordering)
58
- when TestDouble then object.__build_mock_proxy(expectation_ordering)
59
- else
60
- if RSpec::Mocks.configuration.verify_partial_doubles?
61
- VerifyingPartialMockProxy.new(object, expectation_ordering)
62
- else
63
- PartialMockProxy.new(object, expectation_ordering)
64
- end
65
- end
66
- end
92
+ proxies.fetch(id) { proxy_not_found_for(id, object) }
67
93
  end
68
94
 
69
95
  alias ensure_registered proxy_for
@@ -72,6 +98,25 @@ module RSpec
72
98
  proxies.has_key?(id_for object)
73
99
  end
74
100
 
101
+ private
102
+
103
+ def proxy_not_found_for(id, object)
104
+ proxies[id] = case object
105
+ when NilClass then ProxyForNil.new(@expectation_ordering)
106
+ when TestDouble then object.__build_mock_proxy(@expectation_ordering)
107
+ else
108
+ if RSpec::Mocks.configuration.verify_partial_doubles?
109
+ VerifyingPartialDoubleProxy.new(object, @expectation_ordering)
110
+ else
111
+ PartialDoubleProxy.new(object, @expectation_ordering)
112
+ end
113
+ end
114
+ end
115
+
116
+ def any_instance_recorder_not_found_for(id, klass)
117
+ any_instance_recorders[id] = AnyInstance::Recorder.new(klass)
118
+ end
119
+
75
120
  if defined?(::BasicObject) && !::BasicObject.method_defined?(:__id__) # for 1.9.2
76
121
  require 'securerandom'
77
122
 
@@ -91,5 +136,34 @@ module RSpec
91
136
  end
92
137
  end
93
138
  end
139
+
140
+ class NestedSpace < Space
141
+ def initialize(parent)
142
+ @parent = parent
143
+ super()
144
+ end
145
+
146
+ def proxies_of(klass)
147
+ super + @parent.proxies_of(klass)
148
+ end
149
+
150
+ def constant_mutator_for(name)
151
+ super || @parent.constant_mutator_for(name)
152
+ end
153
+
154
+ def registered?(object)
155
+ super || @parent.registered?(object)
156
+ end
157
+
158
+ private
159
+
160
+ def proxy_not_found_for(id, object)
161
+ @parent.proxies[id] || super
162
+ end
163
+
164
+ def any_instance_recorder_not_found_for(id, klass)
165
+ @parent.any_instance_recorders[id] || super
166
+ end
167
+ end
94
168
  end
95
169
  end
@@ -1,3 +1,3 @@
1
1
  require 'rspec/mocks'
2
-
3
- RSpec::Mocks.setup(self)
2
+ include RSpec::Mocks::ExampleMethods
3
+ RSpec::Mocks.setup
@@ -10,11 +10,11 @@ module RSpec
10
10
  end
11
11
 
12
12
  # @api private
13
- def self.warn_unless_should_configured(method_name)
13
+ def self.warn_unless_should_configured(method_name ,replacement = "the new `:expect` syntax or explicitly enable `:should`")
14
14
  if @warn_about_should
15
15
  RSpec.deprecate(
16
16
  "Using `#{method_name}` from rspec-mocks' old `:should` syntax without explicitly enabling the syntax",
17
- :replacement => "the new `:expect` syntax or explicitly enable `:should`"
17
+ :replacement => replacement
18
18
  )
19
19
 
20
20
  @warn_about_should = false
@@ -24,7 +24,7 @@ module RSpec
24
24
  # @api private
25
25
  # Enables the should syntax (`dbl.stub`, `dbl.should_receive`, etc).
26
26
  def self.enable_should(syntax_host = default_should_syntax_host)
27
- @warn_about_should = false
27
+ @warn_about_should = false if syntax_host == default_should_syntax_host
28
28
  return if should_enabled?(syntax_host)
29
29
 
30
30
  syntax_host.class_exec do
@@ -51,7 +51,7 @@ module RSpec
51
51
  end
52
52
 
53
53
  def unstub(message)
54
- ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
54
+ ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__, "`allow(...).to_receive(...).and_call_original` or explicitly enable `:should`")
55
55
  ::RSpec::Mocks.space.proxy_for(self).remove_stub(message)
56
56
  end
57
57
 
@@ -63,7 +63,7 @@ module RSpec
63
63
  def as_null_object
64
64
  ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
65
65
  @_null_object = true
66
- ::RSpec::Mocks.proxy_for(self).as_null_object
66
+ ::RSpec::Mocks.space.proxy_for(self).as_null_object
67
67
  end
68
68
 
69
69
  def null_object?
@@ -73,14 +73,14 @@ module RSpec
73
73
 
74
74
  def received_message?(message, *args, &block)
75
75
  ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
76
- ::RSpec::Mocks.proxy_for(self).received_message?(message, *args, &block)
76
+ ::RSpec::Mocks.space.proxy_for(self).received_message?(message, *args, &block)
77
77
  end
78
78
 
79
79
  unless Class.respond_to? :any_instance
80
80
  Class.class_exec do
81
81
  def any_instance
82
82
  ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
83
- ::RSpec::Mocks.any_instance_recorder_for(self)
83
+ ::RSpec::Mocks.space.any_instance_recorder_for(self)
84
84
  end
85
85
  end
86
86
  end
@@ -124,6 +124,10 @@ module RSpec
124
124
  matcher
125
125
  end
126
126
 
127
+ def receive_message_chain(*messages, &block)
128
+ Matchers::ReceiveMessageChain.new(messages, &block)
129
+ end
130
+
127
131
  def allow(target)
128
132
  AllowanceTarget.new(target)
129
133
  end
@@ -152,6 +156,7 @@ module RSpec
152
156
  syntax_host.class_exec do
153
157
  undef receive
154
158
  undef receive_messages
159
+ undef receive_message_chain
155
160
  undef allow
156
161
  undef expect_any_instance_of
157
162
  undef allow_any_instance_of
@@ -368,7 +373,37 @@ module RSpec
368
373
  # allow(obj).to receive_messages(:speak => "Hello", :meow => "Meow")
369
374
  #
370
375
  # @note This is only available when you have enabled the `expect` syntax.
376
+ #
377
+ # @method receive_message_chain
378
+ # @overload receive_message_chain(method1, method2)
379
+ # @overload receive_message_chain("method1.method2")
380
+ # @overload receive_message_chain(method1, method_to_value_hash)
381
+ #
382
+ # stubs/mocks a chain of messages on an object or test double.
383
+ #
384
+ # ## Warning:
385
+ #
386
+ # Chains can be arbitrarily long, which makes it quite painless to
387
+ # violate the Law of Demeter in violent ways, so you should consider any
388
+ # use of `receive_message_chain` a code smell. Even though not all code smells
389
+ # indicate real problems (think fluent interfaces), `receive_message_chain` still
390
+ # results in brittle examples. For example, if you write
391
+ # `foo.receive_message_chain(:bar, :baz => 37)` in a spec and then the
392
+ # implementation calls `foo.baz.bar`, the stub will not work.
393
+ #
394
+ # @example
395
+ #
396
+ # allow(double).to receive_message_chain("foo.bar") { :baz }
397
+ # allow(double).to receive_message_chain(:foo, :bar => :baz)
398
+ # allow(double).to receive_message_chain(:foo, :bar) { :baz }
399
+ #
400
+ # # Given any of ^^ these three forms ^^:
401
+ # double.foo.bar # => :baz
402
+ #
403
+ # # Common use in Rails/ActiveRecord:
404
+ # allow(Article).to receive_message_chain("recent.published") { [Article.new] }
405
+ #
406
+ # @note This is only available when you have enabled the `expect` syntax.
371
407
  end
372
408
  end
373
409
  end
374
-