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
@@ -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