rspec-mocks 3.0.0.beta1 → 3.0.0.beta2

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