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
@@ -39,20 +39,44 @@ module RSpec
39
39
  end
40
40
  end
41
41
 
42
- def syntax=(values)
43
- if Array(values).include?(:expect)
42
+ # Provides the ability to set either `expect`,
43
+ # `should` or both syntaxes. RSpec uses `expect`
44
+ # syntax by default. This is needed if you want to
45
+ # explicitly enable `should` syntax and/or explicitly
46
+ # disable `expect` syntax.
47
+ #
48
+ # @example
49
+ #
50
+ # RSpec.configure do |rspec|
51
+ # rspec.mock_with :rspec do |mocks|
52
+ # mocks.syntax = [:expect, :should]
53
+ # end
54
+ # end
55
+ #
56
+ def syntax=(*values)
57
+ syntaxes = values.flatten
58
+ if syntaxes.include?(:expect)
44
59
  Syntax.enable_expect
45
60
  else
46
61
  Syntax.disable_expect
47
62
  end
48
63
 
49
- if Array(values).include?(:should)
64
+ if syntaxes.include?(:should)
50
65
  Syntax.enable_should
51
66
  else
52
67
  Syntax.disable_should
53
68
  end
54
69
  end
55
70
 
71
+ # Returns an array with a list of syntaxes
72
+ # that are enabled.
73
+ #
74
+ # @example
75
+ #
76
+ # unless RSpec::Mocks.configuration.syntax.include?(:expect)
77
+ # raise "this RSpec extension gem requires the rspec-mocks `:expect` syntax"
78
+ # end
79
+ #
56
80
  def syntax
57
81
  syntaxes = []
58
82
  syntaxes << :should if Syntax.should_enabled?
@@ -84,7 +108,7 @@ module RSpec
84
108
  end
85
109
 
86
110
  # When set to true, partial mocks will be verified the same as object
87
- # doubles. Any stubs will have their arity checked against the original
111
+ # doubles. Any stubs will have their arguments checked against the original
88
112
  # method, and methods that do not exist cannot be stubbed.
89
113
  def verify_partial_doubles=(val)
90
114
  @verify_partial_doubles = !!val
@@ -1,5 +1,25 @@
1
1
  module RSpec
2
2
  module Mocks
3
+ # @public
4
+ # Raised when a message expectation is not satisfied.
5
+ MockExpectationError = Class.new(Exception)
6
+
7
+ # @public
8
+ # Raised when a test double is used after it has been torn
9
+ # down (typically at the end of an rspec-core example).
10
+ ExpiredTestDoubleError = Class.new(MockExpectationError)
11
+
12
+ # @public
13
+ # Raised when doubles or partial doubles are used outside of the per-test lifecycle.
14
+ OutsideOfExampleError = Class.new(StandardError)
15
+
16
+ # @private
17
+ UnsupportedMatcherError = Class.new(StandardError)
18
+ # @private
19
+ NegationUnsupportedError = Class.new(StandardError)
20
+
21
+ VerifyingDoubleNotDefinedError = Class.new(StandardError)
22
+
3
23
  # @private
4
24
  class ErrorGenerator
5
25
  attr_writer :opts
@@ -56,13 +76,26 @@ module RSpec
56
76
  end
57
77
 
58
78
  # @private
59
- def raise_arity_error(calculator, actual)
60
- __raise "Wrong number of arguments. Expected %s, got %s." % [
61
- calculator.range_description,
62
- actual
79
+ def raise_non_public_error(method_name, visibility)
80
+ raise NoMethodError, "%s method `%s' called on %s" % [
81
+ visibility, method_name, intro
63
82
  ]
64
83
  end
65
84
 
85
+ # @private
86
+ def raise_invalid_arguments_error(verifier)
87
+ __raise verifier.error_message
88
+ end
89
+
90
+ # @private
91
+ def raise_expired_test_double_error
92
+ raise ExpiredTestDoubleError,
93
+ "#{intro} was originally created in one example but has leaked into " +
94
+ "another example and can no longer be used. rspec-mocks' doubles are " +
95
+ "designed to only last for one example, and you need to create a new " +
96
+ "one in each example you wish to use it for."
97
+ end
98
+
66
99
  # @private
67
100
  def received_part_of_expectation_error(actual_received_count, *args)
68
101
  "received: #{count_message(actual_received_count)}" +
@@ -112,14 +145,14 @@ module RSpec
112
145
  end
113
146
 
114
147
  # @private
115
- def raise_wrong_arity_error(args_to_yield, arity)
116
- __raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with arity of #{arity}"
148
+ def raise_wrong_arity_error(args_to_yield, signature)
149
+ __raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with #{signature.description}"
117
150
  end
118
151
 
119
152
  # @private
120
- def raise_only_valid_on_a_partial_mock(method)
121
- __raise "#{intro} is a pure mock object. `#{method}` is only " +
122
- "available on a partial mock object."
153
+ def raise_only_valid_on_a_partial_double(method)
154
+ __raise "#{intro} is a pure test double. `#{method}` is only " +
155
+ "available on a partial double."
123
156
  end
124
157
 
125
158
  # @private
@@ -189,12 +222,12 @@ module RSpec
189
222
  end
190
223
 
191
224
  def count_message(count, expectation_count_type=nil)
192
- return "at least #{pretty_print(count.abs)}" if count < 0 || expectation_count_type == :at_least
193
- return "at most #{pretty_print(count)}" if expectation_count_type == :at_most
194
- return pretty_print(count)
225
+ return "at least #{times(count.abs)}" if count < 0 || expectation_count_type == :at_least
226
+ return "at most #{times(count)}" if expectation_count_type == :at_most
227
+ return times(count)
195
228
  end
196
229
 
197
- def pretty_print(count)
230
+ def times(count)
198
231
  "#{count} time#{count == 1 ? '' : 's'}"
199
232
  end
200
233
 
@@ -12,9 +12,9 @@ module RSpec
12
12
  # @param name [String/Symbol] (optional) used in
13
13
  # clarify intent
14
14
  # @param stubs (Hash) (optional) hash of message/return-value pairs
15
- # @return (Mock)
15
+ # @return (Double)
16
16
  #
17
- # Constructs an instance of [RSpec::Mocks::Mock](RSpec::Mocks::Mock) configured
17
+ # Constructs an instance of [RSpec::Mocks::Double](RSpec::Mocks::Double) configured
18
18
  # with an optional name, used for reporting in failure messages, and an optional
19
19
  # hash of message/return-value pairs.
20
20
  #
@@ -28,7 +28,7 @@ module RSpec
28
28
  # card.rank #=> "A"
29
29
  #
30
30
  def double(*args)
31
- declare_double(Mock, *args)
31
+ ExampleMethods.declare_double(Double, *args)
32
32
  end
33
33
 
34
34
  # @overload instance_double(doubled_class)
@@ -43,7 +43,7 @@ module RSpec
43
43
  # [double](double).
44
44
  def instance_double(doubled_class, *args)
45
45
  ref = ObjectReference.for(doubled_class)
46
- declare_verifying_double(InstanceVerifyingDouble, ref, *args)
46
+ ExampleMethods.declare_verifying_double(InstanceVerifyingDouble, ref, *args)
47
47
  end
48
48
 
49
49
  # @overload class_double(doubled_class)
@@ -58,7 +58,7 @@ module RSpec
58
58
  # [double](double).
59
59
  def class_double(doubled_class, *args)
60
60
  ref = ObjectReference.for(doubled_class)
61
- declare_verifying_double(ClassVerifyingDouble, ref, *args)
61
+ ExampleMethods.declare_verifying_double(ClassVerifyingDouble, ref, *args)
62
62
  end
63
63
 
64
64
  # @overload object_double(object_or_name)
@@ -73,7 +73,7 @@ module RSpec
73
73
  # for verification. In all other ways it behaves like a [double](double).
74
74
  def object_double(object_or_name, *args)
75
75
  ref = ObjectReference.for(object_or_name, :allow_direct_object_refs)
76
- declare_verifying_double(ObjectVerifyingDouble, ref, *args)
76
+ ExampleMethods.declare_verifying_double(ObjectVerifyingDouble, ref, *args)
77
77
  end
78
78
 
79
79
  # Disables warning messages about expectations being set on nil.
@@ -164,6 +164,7 @@ module RSpec
164
164
  Matchers::HaveReceived.new(method_name, &block)
165
165
  end
166
166
 
167
+ # @api private
167
168
  def self.included(klass)
168
169
  klass.class_exec do
169
170
  # This gets mixed in so that if `RSpec::Matchers` is included in
@@ -172,14 +173,13 @@ module RSpec
172
173
  end
173
174
  end
174
175
 
175
- private
176
-
177
- def declare_verifying_double(type, ref, *args)
176
+ # @api private
177
+ def self.declare_verifying_double(type, ref, *args)
178
178
  if RSpec::Mocks.configuration.verify_doubled_constant_names? &&
179
179
  !ref.defined?
180
180
 
181
- raise NameError,
182
- "#{ref.name} is not a defined constant. " +
181
+ raise VerifyingDoubleNotDefinedError,
182
+ "#{ref.description} is not a defined constant. " +
183
183
  "Perhaps you misspelt it? " +
184
184
  "Disable check with verify_doubled_constant_names configuration option."
185
185
  end
@@ -187,7 +187,8 @@ module RSpec
187
187
  declare_double(type, ref, *args)
188
188
  end
189
189
 
190
- def declare_double(type, *args)
190
+ # @api private
191
+ def self.declare_double(type, *args)
191
192
  args << {} unless Hash === args.last
192
193
  type.new(*args)
193
194
  end
@@ -3,7 +3,7 @@ module Marshal
3
3
  # Duplicates any mock objects before serialization. Otherwise,
4
4
  # serialization will fail because methods exist on the singleton class.
5
5
  def dump_with_mocks(object, *rest)
6
- if ::RSpec::Mocks.space.nil? || !::RSpec::Mocks.space.registered?(object) || NilClass === object
6
+ if !::RSpec::Mocks.space.registered?(object) || NilClass === object
7
7
  dump_without_mocks(object, *rest)
8
8
  else
9
9
  dump_without_mocks(object.dup, *rest)
@@ -9,19 +9,17 @@ require 'rspec/mocks/method_double'
9
9
  require 'rspec/mocks/argument_matchers'
10
10
  require 'rspec/mocks/example_methods'
11
11
  require 'rspec/mocks/proxy'
12
- require 'rspec/mocks/proxy_for_nil'
13
12
  require 'rspec/mocks/test_double'
14
- require 'rspec/mocks/mock'
15
13
  require 'rspec/mocks/argument_list_matcher'
16
14
  require 'rspec/mocks/message_expectation'
17
15
  require 'rspec/mocks/order_group'
18
- require 'rspec/mocks/errors'
19
16
  require 'rspec/mocks/error_generator'
20
17
  require 'rspec/mocks/space'
21
18
  require 'rspec/mocks/extensions/marshal'
22
19
  require 'rspec/mocks/any_instance/chain'
23
20
  require 'rspec/mocks/any_instance/stub_chain'
24
21
  require 'rspec/mocks/any_instance/stub_chain_chain'
22
+ require 'rspec/mocks/any_instance/expect_chain_chain'
25
23
  require 'rspec/mocks/any_instance/expectation_chain'
26
24
  require 'rspec/mocks/any_instance/message_chains'
27
25
  require 'rspec/mocks/any_instance/recorder'
@@ -29,7 +27,8 @@ require 'rspec/mocks/mutate_const'
29
27
  require 'rspec/mocks/matchers/have_received'
30
28
  require 'rspec/mocks/matchers/receive'
31
29
  require 'rspec/mocks/matchers/receive_messages'
32
- require 'rspec/mocks/stub_chain'
30
+ require 'rspec/mocks/matchers/receive_message_chain'
31
+ require 'rspec/mocks/message_chain'
33
32
  require 'rspec/mocks/targets'
34
33
  require 'rspec/mocks/syntax'
35
34
  require 'rspec/mocks/configuration'
@@ -54,7 +54,7 @@ module RSpec
54
54
  # @private
55
55
  def stash
56
56
  return if !method_defined_directly_on_klass?
57
- @original_method ||= ::RSpec::Mocks.method_handle_for(@object, @method)
57
+ @original_method ||= ::RSpec::Support.method_handle_for(@object, @method)
58
58
  end
59
59
 
60
60
  # @private
@@ -79,7 +79,7 @@ module RSpec
79
79
 
80
80
  # @private
81
81
  def method_defined_on_klass?(klass = @klass)
82
- klass.method_defined?(@method) || klass.private_method_defined?(@method)
82
+ MethodReference.method_defined_at_any_visibility?(klass, @method)
83
83
  end
84
84
 
85
85
  def method_owned_by_klass?
@@ -109,4 +109,3 @@ module RSpec
109
109
  end
110
110
  end
111
111
  end
112
-
@@ -1,6 +1,7 @@
1
1
  module RSpec
2
2
  module Mocks
3
3
  module Matchers
4
+ # @private
4
5
  class HaveReceived
5
6
  COUNT_CONSTRAINTS = %w(exactly at_least at_most times once twice)
6
7
  ARGS_CONSTRAINTS = %w(with)
@@ -35,7 +36,7 @@ module RSpec
35
36
  generate_failure_message
36
37
  end
37
38
 
38
- def negative_failure_message
39
+ def failure_message_when_negated
39
40
  generate_failure_message
40
41
  end
41
42
 
@@ -53,9 +54,11 @@ module RSpec
53
54
  private
54
55
 
55
56
  def expect
56
- expectation = mock_proxy.build_expectation(@method_name)
57
- apply_constraints_to expectation
58
- expectation
57
+ @expectation ||= begin
58
+ expectation = mock_proxy.build_expectation(@method_name)
59
+ apply_constraints_to expectation
60
+ expectation
61
+ end
59
62
  end
60
63
 
61
64
  def apply_constraints_to(expectation)
@@ -90,10 +93,9 @@ module RSpec
90
93
  end
91
94
 
92
95
  def mock_proxy
93
- RSpec::Mocks.proxy_for(@subject)
96
+ RSpec::Mocks.space.proxy_for(@subject)
94
97
  end
95
98
  end
96
99
  end
97
100
  end
98
101
  end
99
-
@@ -1,17 +1,13 @@
1
1
  module RSpec
2
2
  module Mocks
3
3
  module Matchers
4
+ # @private
4
5
  class Receive
5
6
  def initialize(message, block)
6
7
  @message = message
7
8
  @block = block
8
9
  @recorded_customizations = []
9
-
10
- # MRI, JRuby and RBX report the caller inconsistently; MRI
11
- # reports an extra "in `new'" line in the backtrace that the
12
- # others do not include. The safest way to find the right
13
- # line is to search for the first line BEFORE rspec/mocks/syntax.rb.
14
- @backtrace_line = CallerFilter.first_non_rspec_line
10
+ @backtrace_line = CallerFilter.first_non_rspec_line
15
11
  end
16
12
 
17
13
  def name
@@ -27,7 +23,7 @@ module RSpec
27
23
  def setup_negative_expectation(subject, &block)
28
24
  # ensure `never` goes first for cases like `never.and_return(5)`,
29
25
  # where `and_return` is meant to raise an error
30
- @recorded_customizations.unshift Customization.new(:never, [], nil)
26
+ @recorded_customizations.unshift ExpectationCustomization.new(:never, [], nil)
31
27
 
32
28
  warn_if_any_instance("expect", subject)
33
29
 
@@ -56,7 +52,7 @@ module RSpec
56
52
  next if method_defined?(method)
57
53
 
58
54
  define_method(method) do |*args, &block|
59
- @recorded_customizations << Customization.new(method, args, block)
55
+ @recorded_customizations << ExpectationCustomization.new(method, args, block)
60
56
  self
61
57
  end
62
58
  end
@@ -75,17 +71,19 @@ module RSpec
75
71
  end
76
72
 
77
73
  def setup_mock_proxy_method_substitute(subject, method, block)
78
- proxy = ::RSpec::Mocks.proxy_for(subject)
74
+ proxy = ::RSpec::Mocks.space.proxy_for(subject)
79
75
  setup_method_substitute(proxy, method, block, @backtrace_line)
80
76
  end
81
77
 
82
78
  def setup_any_instance_method_substitute(subject, method, block)
83
- any_instance_recorder = ::RSpec::Mocks.any_instance_recorder_for(subject)
79
+ any_instance_recorder = ::RSpec::Mocks.space.any_instance_recorder_for(subject)
84
80
  setup_method_substitute(any_instance_recorder, method, block)
85
81
  end
86
82
 
87
83
  def setup_method_substitute(host, method, block, *args)
88
84
  args << @message.to_sym
85
+ block = move_block_to_last_customization(block)
86
+
89
87
  expectation = host.__send__(method, *args, &(@block || block))
90
88
 
91
89
  @recorded_customizations.each do |customization|
@@ -94,19 +92,29 @@ module RSpec
94
92
  expectation
95
93
  end
96
94
 
97
- class Customization
98
- def initialize(method_name, args, block)
99
- @method_name = method_name
100
- @args = args
101
- @block = block
102
- end
95
+ def move_block_to_last_customization(block)
96
+ last = @recorded_customizations.last
97
+ return block unless last
103
98
 
104
- def playback_onto(expectation)
105
- expectation.__send__(@method_name, *@args, &@block)
106
- end
99
+ last.block ||= block
100
+ nil
107
101
  end
108
102
  end
109
103
  end
104
+
105
+ # @private
106
+ class ExpectationCustomization
107
+ attr_accessor :block
108
+
109
+ def initialize(method_name, args, block)
110
+ @method_name = method_name
111
+ @args = args
112
+ @block = block
113
+ end
114
+
115
+ def playback_onto(expectation)
116
+ expectation.__send__(@method_name, *@args, &@block)
117
+ end
118
+ end
110
119
  end
111
120
  end
112
-
@@ -0,0 +1,65 @@
1
+ module RSpec
2
+ module Mocks
3
+ module Matchers
4
+ # @private
5
+ class ReceiveMessageChain
6
+ def initialize(chain, &block)
7
+ @chain = chain
8
+ @block = block
9
+ @recorded_customizations = []
10
+ end
11
+
12
+ [:and_return, :and_throw, :and_raise, :and_yield, :and_call_original].each do |msg|
13
+ define_method(msg) do |*args, &block|
14
+ @recorded_customizations << ExpectationCustomization.new(msg, args, block)
15
+ self
16
+ end
17
+ end
18
+
19
+ def name
20
+ "receive_message_chain"
21
+ end
22
+
23
+ def setup_allowance(subject, &block)
24
+ chain = StubChain.stub_chain_on(subject, *@chain, &(@block || block))
25
+ replay_customizations(chain)
26
+ end
27
+
28
+ def setup_any_instance_allowance(subject, &block)
29
+ recorder = ::RSpec::Mocks.space.any_instance_recorder_for(subject)
30
+ chain = recorder.stub_chain(*@chain, &(@block || block))
31
+ replay_customizations(chain)
32
+ end
33
+
34
+ def setup_any_instance_expectation(subject, &block)
35
+ recorder = ::RSpec::Mocks.space.any_instance_recorder_for(subject)
36
+ chain = recorder.expect_chain(*@chain, &(@block || block))
37
+ replay_customizations(chain)
38
+ end
39
+
40
+ def setup_expectation(subject, &block)
41
+ chain = ExpectChain.expect_chain_on(subject, *@chain, &(@block || block))
42
+ replay_customizations(chain)
43
+ end
44
+
45
+ def setup_negative_expectation(*args)
46
+ raise NegationUnsupportedError.new(
47
+ "`expect(...).not_to receive_message_chain` is not supported " +
48
+ "since it doesn't really make sense. What would it even mean?"
49
+ )
50
+ end
51
+
52
+ alias matches? setup_expectation
53
+ alias does_not_match? setup_negative_expectation
54
+
55
+ private
56
+
57
+ def replay_customizations(chain)
58
+ @recorded_customizations.each do |customization|
59
+ customization.playback_onto(chain)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end