rspec-mocks 2.11.3 → 3.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +1 -1
  5. data/Changelog.md +989 -21
  6. data/{License.txt → LICENSE.md} +5 -3
  7. data/README.md +260 -73
  8. data/lib/rspec/mocks/any_instance/chain.rb +58 -24
  9. data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
  10. data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +31 -0
  11. data/lib/rspec/mocks/any_instance/expectation_chain.rb +23 -30
  12. data/lib/rspec/mocks/any_instance/message_chains.rb +38 -15
  13. data/lib/rspec/mocks/any_instance/proxy.rb +116 -0
  14. data/lib/rspec/mocks/any_instance/recorder.rb +155 -59
  15. data/lib/rspec/mocks/any_instance/stub_chain.rb +33 -19
  16. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +10 -12
  17. data/lib/rspec/mocks/any_instance.rb +11 -81
  18. data/lib/rspec/mocks/argument_list_matcher.rb +59 -37
  19. data/lib/rspec/mocks/argument_matchers.rb +233 -149
  20. data/lib/rspec/mocks/configuration.rb +212 -0
  21. data/lib/rspec/mocks/error_generator.rb +304 -49
  22. data/lib/rspec/mocks/example_methods.rb +361 -22
  23. data/lib/rspec/mocks/instance_method_stasher.rb +146 -0
  24. data/lib/rspec/mocks/marshal_extension.rb +41 -0
  25. data/lib/rspec/mocks/matchers/expectation_customization.rb +20 -0
  26. data/lib/rspec/mocks/matchers/have_received.rb +134 -0
  27. data/lib/rspec/mocks/matchers/receive.rb +133 -0
  28. data/lib/rspec/mocks/matchers/receive_message_chain.rb +82 -0
  29. data/lib/rspec/mocks/matchers/receive_messages.rb +77 -0
  30. data/lib/rspec/mocks/message_chain.rb +87 -0
  31. data/lib/rspec/mocks/message_expectation.rb +648 -314
  32. data/lib/rspec/mocks/method_double.rb +185 -58
  33. data/lib/rspec/mocks/method_reference.rb +202 -0
  34. data/lib/rspec/mocks/minitest_integration.rb +68 -0
  35. data/lib/rspec/mocks/mutate_const.rb +339 -0
  36. data/lib/rspec/mocks/object_reference.rb +149 -0
  37. data/lib/rspec/mocks/order_group.rb +48 -7
  38. data/lib/rspec/mocks/proxy.rb +405 -74
  39. data/lib/rspec/mocks/space.rb +219 -15
  40. data/lib/rspec/mocks/standalone.rb +2 -2
  41. data/lib/rspec/mocks/syntax.rb +325 -0
  42. data/lib/rspec/mocks/targets.rb +124 -0
  43. data/lib/rspec/mocks/test_double.rb +125 -57
  44. data/lib/rspec/mocks/verifying_double.rb +121 -0
  45. data/lib/rspec/mocks/verifying_message_expectation.rb +54 -0
  46. data/lib/rspec/mocks/verifying_proxy.rb +220 -0
  47. data/lib/rspec/mocks/version.rb +3 -1
  48. data/lib/rspec/mocks.rb +121 -27
  49. data.tar.gz.sig +0 -0
  50. metadata +178 -253
  51. metadata.gz.sig +3 -0
  52. data/features/README.md +0 -67
  53. data/features/Scope.md +0 -17
  54. data/features/Upgrade.md +0 -22
  55. data/features/argument_matchers/README.md +0 -27
  56. data/features/argument_matchers/explicit.feature +0 -60
  57. data/features/argument_matchers/general_matchers.feature +0 -85
  58. data/features/argument_matchers/type_matchers.feature +0 -27
  59. data/features/message_expectations/README.md +0 -69
  60. data/features/message_expectations/any_instance.feature +0 -21
  61. data/features/message_expectations/block_local_expectations.feature.pending +0 -55
  62. data/features/message_expectations/expect_message.feature +0 -94
  63. data/features/message_expectations/receive_counts.feature +0 -209
  64. data/features/message_expectations/warn_when_expectation_is_set_on_nil.feature +0 -50
  65. data/features/method_stubs/README.md +0 -47
  66. data/features/method_stubs/any_instance.feature +0 -133
  67. data/features/method_stubs/as_null_object.feature +0 -35
  68. data/features/method_stubs/simple_return_value.feature +0 -64
  69. data/features/method_stubs/stub_chain.feature +0 -51
  70. data/features/method_stubs/stub_implementation.feature +0 -26
  71. data/features/method_stubs/to_ary.feature +0 -47
  72. data/features/outside_rspec/configuration.feature +0 -82
  73. data/features/outside_rspec/standalone.feature +0 -32
  74. data/features/step_definitions/additional_cli_steps.rb +0 -4
  75. data/features/stubbing_constants/README.md +0 -62
  76. data/features/stubbing_constants/stub_defined_constant.feature +0 -79
  77. data/features/stubbing_constants/stub_undefined_constant.feature +0 -50
  78. data/features/support/env.rb +0 -6
  79. data/lib/rspec/mocks/errors.rb +0 -12
  80. data/lib/rspec/mocks/extensions/instance_exec.rb +0 -34
  81. data/lib/rspec/mocks/extensions/marshal.rb +0 -23
  82. data/lib/rspec/mocks/extensions/psych.rb +0 -23
  83. data/lib/rspec/mocks/framework.rb +0 -21
  84. data/lib/rspec/mocks/methods.rb +0 -155
  85. data/lib/rspec/mocks/mock.rb +0 -7
  86. data/lib/rspec/mocks/serialization.rb +0 -34
  87. data/lib/rspec/mocks/stashed_instance_method.rb +0 -60
  88. data/lib/rspec/mocks/stub_const.rb +0 -332
  89. data/lib/spec/mocks.rb +0 -2
  90. data/spec/rspec/mocks/and_yield_spec.rb +0 -114
  91. data/spec/rspec/mocks/any_instance/message_chains_spec.rb +0 -40
  92. data/spec/rspec/mocks/any_instance_spec.rb +0 -877
  93. data/spec/rspec/mocks/any_number_of_times_spec.rb +0 -30
  94. data/spec/rspec/mocks/argument_expectation_spec.rb +0 -34
  95. data/spec/rspec/mocks/at_least_spec.rb +0 -142
  96. data/spec/rspec/mocks/at_most_spec.rb +0 -90
  97. data/spec/rspec/mocks/block_return_value_spec.rb +0 -53
  98. data/spec/rspec/mocks/bug_report_10260_spec.rb +0 -8
  99. data/spec/rspec/mocks/bug_report_10263_spec.rb +0 -25
  100. data/spec/rspec/mocks/bug_report_11545_spec.rb +0 -32
  101. data/spec/rspec/mocks/bug_report_496_spec.rb +0 -17
  102. data/spec/rspec/mocks/bug_report_600_spec.rb +0 -22
  103. data/spec/rspec/mocks/bug_report_7611_spec.rb +0 -16
  104. data/spec/rspec/mocks/bug_report_8165_spec.rb +0 -31
  105. data/spec/rspec/mocks/bug_report_830_spec.rb +0 -21
  106. data/spec/rspec/mocks/bug_report_957_spec.rb +0 -22
  107. data/spec/rspec/mocks/double_spec.rb +0 -12
  108. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +0 -95
  109. data/spec/rspec/mocks/hash_excluding_matcher_spec.rb +0 -67
  110. data/spec/rspec/mocks/hash_including_matcher_spec.rb +0 -90
  111. data/spec/rspec/mocks/mock_ordering_spec.rb +0 -103
  112. data/spec/rspec/mocks/mock_space_spec.rb +0 -58
  113. data/spec/rspec/mocks/mock_spec.rb +0 -730
  114. data/spec/rspec/mocks/multiple_return_value_spec.rb +0 -119
  115. data/spec/rspec/mocks/nil_expectation_warning_spec.rb +0 -62
  116. data/spec/rspec/mocks/null_object_mock_spec.rb +0 -106
  117. data/spec/rspec/mocks/once_counts_spec.rb +0 -52
  118. data/spec/rspec/mocks/options_hash_spec.rb +0 -35
  119. data/spec/rspec/mocks/partial_mock_spec.rb +0 -171
  120. data/spec/rspec/mocks/partial_mock_using_mocks_directly_spec.rb +0 -95
  121. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +0 -142
  122. data/spec/rspec/mocks/precise_counts_spec.rb +0 -68
  123. data/spec/rspec/mocks/record_messages_spec.rb +0 -26
  124. data/spec/rspec/mocks/serialization_spec.rb +0 -111
  125. data/spec/rspec/mocks/stash_spec.rb +0 -27
  126. data/spec/rspec/mocks/stashed_instance_method_spec.rb +0 -53
  127. data/spec/rspec/mocks/stub_chain_spec.rb +0 -154
  128. data/spec/rspec/mocks/stub_const_spec.rb +0 -334
  129. data/spec/rspec/mocks/stub_implementation_spec.rb +0 -81
  130. data/spec/rspec/mocks/stub_spec.rb +0 -247
  131. data/spec/rspec/mocks/stubbed_message_expectations_spec.rb +0 -47
  132. data/spec/rspec/mocks/test_double_spec.rb +0 -57
  133. data/spec/rspec/mocks/to_ary_spec.rb +0 -40
  134. data/spec/rspec/mocks/twice_counts_spec.rb +0 -66
  135. data/spec/rspec/mocks_spec.rb +0 -51
  136. data/spec/spec_helper.rb +0 -21
@@ -1,63 +1,51 @@
1
+ RSpec::Support.require_rspec_support 'mutex'
2
+
1
3
  module RSpec
2
4
  module Mocks
5
+ # A message expectation that only allows concrete return values to be set
6
+ # for a message. While this same effect can be achieved using a standard
7
+ # MessageExpectation, this version is much faster and so can be used as an
8
+ # optimization.
9
+ #
10
+ # @private
11
+ class SimpleMessageExpectation
12
+ def initialize(message, response, error_generator, backtrace_line=nil)
13
+ @message, @response, @error_generator, @backtrace_line = message.to_sym, response, error_generator, backtrace_line
14
+ @received = false
15
+ end
3
16
 
4
- class MessageExpectation
5
- # @private
6
- attr_accessor :error_generator
7
- attr_reader :message
8
- attr_writer :expected_received_count, :expected_from, :argument_list_matcher
9
- protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=
17
+ def invoke(*_)
18
+ @received = true
19
+ @response
20
+ end
10
21
 
11
- # @private
12
- def initialize(error_generator, expectation_ordering, expected_from, message, expected_received_count=1, opts={}, &implementation)
13
- @error_generator = error_generator
14
- @error_generator.opts = opts
15
- @expected_from = expected_from
16
- @message = message
17
- @actual_received_count = 0
18
- @expected_received_count = expected_received_count
19
- @argument_list_matcher = ArgumentListMatcher.new(ArgumentMatchers::AnyArgsMatcher.new)
20
- @consecutive = false
21
- @exception_to_raise = nil
22
- @args_to_throw = []
23
- @order_group = expectation_ordering
24
- @at_least = @at_most = @exactly = nil
25
- @args_to_yield = []
26
- @failed_fast = nil
27
- @args_to_yield_were_cloned = false
28
- @eval_context = nil
29
- @implementation = implementation
30
- end
31
-
32
- def implementation=(implementation)
33
- @consecutive = false
34
- @implementation = implementation
35
- end
36
- protected :implementation=
22
+ def matches?(message, *_)
23
+ @message == message.to_sym
24
+ end
37
25
 
38
- # @private
39
- def build_child(expected_from, expected_received_count, opts={}, &implementation)
40
- child = clone
41
- child.expected_from = expected_from
42
- child.implementation = implementation if implementation
43
- child.expected_received_count = expected_received_count
44
- child.clear_actual_received_count!
45
- new_gen = error_generator.clone
46
- new_gen.opts = opts
47
- child.error_generator = new_gen
48
- child.clone_args_to_yield(*@args_to_yield)
49
- child.argument_list_matcher = ArgumentListMatcher.new(ArgumentMatchers::AnyArgsMatcher.new)
50
- child
26
+ def called_max_times?
27
+ false
51
28
  end
52
29
 
53
- # @private
54
- def expected_args
55
- @argument_list_matcher.expected_args
30
+ def verify_messages_received
31
+ return if @received
32
+ @error_generator.raise_expectation_error(
33
+ @message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil, [], @backtrace_line
34
+ )
35
+ end
36
+
37
+ def unadvise(_)
56
38
  end
39
+ end
40
+
41
+ # Represents an individual method stub or message expectation. The methods
42
+ # defined here can be used to configure how it behaves. The methods return
43
+ # `self` so that they can be chained together to form a fluent interface.
44
+ class MessageExpectation
45
+ # @!group Configuring Responses
57
46
 
58
47
  # @overload and_return(value)
59
48
  # @overload and_return(first_value, second_value)
60
- # @overload and_return(&block)
61
49
  #
62
50
  # Tells the object to return a value when it receives the message. Given
63
51
  # more than one value, the first value is returned the first time the
@@ -65,58 +53,140 @@ module RSpec
65
53
  # etc.
66
54
  #
67
55
  # If the message is received more times than there are values, the last
68
- # value is received for every subsequent call.
69
- #
70
- # The block format is still supported, but is unofficially deprecated in
71
- # favor of just passing a block to the stub method.
56
+ # value is returned for every subsequent call.
72
57
  #
58
+ # @return [nil] No further chaining is supported after this.
73
59
  # @example
74
- #
75
- # counter.stub(:count).and_return(1)
60
+ # allow(counter).to receive(:count).and_return(1)
76
61
  # counter.count # => 1
77
62
  # counter.count # => 1
78
63
  #
79
- # counter.stub(:count).and_return(1,2,3)
64
+ # allow(counter).to receive(:count).and_return(1,2,3)
80
65
  # counter.count # => 1
81
66
  # counter.count # => 2
82
67
  # counter.count # => 3
83
68
  # counter.count # => 3
84
69
  # counter.count # => 3
85
70
  # # etc
71
+ def and_return(first_value, *values)
72
+ raise_already_invoked_error_if_necessary(__method__)
73
+ if negative?
74
+ raise "`and_return` is not supported with negative message expectations"
75
+ end
76
+
77
+ if block_given?
78
+ raise ArgumentError, "Implementation blocks aren't supported with `and_return`"
79
+ end
80
+
81
+ values.unshift(first_value)
82
+ @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least)
83
+ self.terminal_implementation_action = AndReturnImplementation.new(values)
84
+
85
+ nil
86
+ end
87
+
88
+ # Tells the object to invoke a Proc when it receives the message. Given
89
+ # more than one value, the result of the first Proc is returned the first
90
+ # time the message is received, the result of the second Proc is returned
91
+ # the next time, etc, etc.
86
92
  #
87
- # # Supported, but ...
88
- # counter.stub(:count).and_return { 1 }
89
- # counter.count # => 1
93
+ # If the message is received more times than there are Procs, the result of
94
+ # the last Proc is returned for every subsequent call.
90
95
  #
91
- # # ... this is prefered
92
- # counter.stub(:count) { 1 }
93
- # counter.count # => 1
94
- def and_return(*values, &implementation)
95
- @expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 and @at_least)
96
- @consecutive = true if values.size > 1
97
- @implementation = implementation || build_implementation(values)
96
+ # @return [nil] No further chaining is supported after this.
97
+ # @example
98
+ # allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout })
99
+ # api.get_foo # => raises ApiTimeout
100
+ # api.get_foo # => raises ApiTimeout
101
+ #
102
+ # allow(api).to receive(:get_foo).and_invoke(-> { raise ApiTimeout }, -> { raise ApiTimeout }, -> { :a_foo })
103
+ # api.get_foo # => raises ApiTimeout
104
+ # api.get_foo # => rasies ApiTimeout
105
+ # api.get_foo # => :a_foo
106
+ # api.get_foo # => :a_foo
107
+ # api.get_foo # => :a_foo
108
+ # # etc
109
+ def and_invoke(first_proc, *procs)
110
+ raise_already_invoked_error_if_necessary(__method__)
111
+ if negative?
112
+ raise "`and_invoke` is not supported with negative message expectations"
113
+ end
114
+
115
+ if block_given?
116
+ raise ArgumentError, "Implementation blocks aren't supported with `and_invoke`"
117
+ end
118
+
119
+ procs.unshift(first_proc)
120
+ if procs.any? { |p| !p.respond_to?(:call) }
121
+ raise ArgumentError, "Arguments to `and_invoke` must be callable."
122
+ end
123
+
124
+ @expected_received_count = [@expected_received_count, procs.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least)
125
+ self.terminal_implementation_action = AndInvokeImplementation.new(procs)
126
+
127
+ nil
128
+ end
129
+
130
+ # Tells the object to delegate to the original unmodified method
131
+ # when it receives the message.
132
+ #
133
+ # @note This is only available on partial doubles.
134
+ #
135
+ # @return [nil] No further chaining is supported after this.
136
+ # @example
137
+ # expect(counter).to receive(:increment).and_call_original
138
+ # original_count = counter.count
139
+ # counter.increment
140
+ # expect(counter.count).to eq(original_count + 1)
141
+ def and_call_original
142
+ block = lambda do |original, *args, &b|
143
+ original.call(*args, &b)
144
+ end
145
+ block = block.ruby2_keywords if block.respond_to?(:ruby2_keywords)
146
+
147
+ wrap_original(__method__, &block)
148
+ end
149
+
150
+ # Decorates the stubbed method with the supplied block. The original
151
+ # unmodified method is passed to the block along with any method call
152
+ # arguments so you can delegate to it, whilst still being able to
153
+ # change what args are passed to it and/or change the return value.
154
+ #
155
+ # @note This is only available on partial doubles.
156
+ #
157
+ # @return [nil] No further chaining is supported after this.
158
+ # @example
159
+ # expect(api).to receive(:large_list).and_wrap_original do |original_method, *args, &block|
160
+ # original_method.call(*args, &block).first(10)
161
+ # end
162
+ def and_wrap_original(&block)
163
+ wrap_original(__method__, &block)
98
164
  end
99
165
 
100
166
  # @overload and_raise
101
167
  # @overload and_raise(ExceptionClass)
168
+ # @overload and_raise(ExceptionClass, message)
102
169
  # @overload and_raise(exception_instance)
103
170
  #
104
171
  # Tells the object to raise an exception when the message is received.
105
172
  #
173
+ # @return [nil] No further chaining is supported after this.
106
174
  # @note
107
- #
108
175
  # When you pass an exception class, the MessageExpectation will raise
109
- # an instance of it, creating it with `new`. If the exception class
110
- # initializer requires any parameters, you must pass in an instance and
111
- # not the class.
176
+ # an instance of it, creating it with `exception` and passing `message`
177
+ # if specified. If the exception class initializer requires more than
178
+ # one parameters, you must pass in an instance and not the class,
179
+ # otherwise this method will raise an ArgumentError exception.
112
180
  #
113
181
  # @example
114
- #
115
- # car.stub(:go).and_raise
116
- # car.stub(:go).and_raise(OutOfGas)
117
- # car.stub(:go).and_raise(OutOfGas.new(2, :oz))
118
- def and_raise(exception=RuntimeError)
119
- @exception_to_raise = exception
182
+ # allow(car).to receive(:go).and_raise
183
+ # allow(car).to receive(:go).and_raise(OutOfGas)
184
+ # allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive")
185
+ # allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz))
186
+ def and_raise(*args)
187
+ raise_already_invoked_error_if_necessary(__method__)
188
+ self.terminal_implementation_action = Proc.new { raise(*args) }
189
+ nil
120
190
  end
121
191
 
122
192
  # @overload and_throw(symbol)
@@ -125,185 +195,47 @@ module RSpec
125
195
  # Tells the object to throw a symbol (with the object if that form is
126
196
  # used) when the message is received.
127
197
  #
198
+ # @return [nil] No further chaining is supported after this.
128
199
  # @example
129
- #
130
- # car.stub(:go).and_throw(:out_of_gas)
131
- # car.stub(:go).and_throw(:out_of_gas, :level => 0.1)
132
- def and_throw(symbol, object = nil)
133
- @args_to_throw = [symbol, object].compact
200
+ # allow(car).to receive(:go).and_throw(:out_of_gas)
201
+ # allow(car).to receive(:go).and_throw(:out_of_gas, :level => 0.1)
202
+ def and_throw(*args)
203
+ raise_already_invoked_error_if_necessary(__method__)
204
+ self.terminal_implementation_action = Proc.new { throw(*args) }
205
+ nil
134
206
  end
135
207
 
136
208
  # Tells the object to yield one or more args to a block when the message
137
209
  # is received.
138
210
  #
211
+ # @return [MessageExpectation] self, to support further chaining.
139
212
  # @example
140
- #
141
213
  # stream.stub(:open).and_yield(StringIO.new)
142
214
  def and_yield(*args, &block)
143
- if @args_to_yield_were_cloned
144
- @args_to_yield.clear
145
- @args_to_yield_were_cloned = false
146
- end
215
+ raise_already_invoked_error_if_necessary(__method__)
216
+ yield @eval_context = Object.new if block
147
217
 
148
- yield @eval_context = Object.new.extend(RSpec::Mocks::InstanceExec) if block
218
+ # Initialize args to yield now that it's being used, see also: comment
219
+ # in constructor.
220
+ @args_to_yield ||= []
149
221
 
150
222
  @args_to_yield << args
223
+ self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
151
224
  self
152
225
  end
226
+ # @!endgroup
153
227
 
154
- # @private
155
- def matches?(message, *args)
156
- @message == message && @argument_list_matcher.args_match?(*args)
157
- end
158
-
159
- # @private
160
- def invoke(*args, &block)
161
- if (@expected_received_count == 0 && !@at_least) || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
162
- @actual_received_count += 1
163
- @failed_fast = true
164
- @error_generator.raise_expectation_error(@message, @expected_received_count, @actual_received_count, *args)
165
- end
166
-
167
- @order_group.handle_order_constraint self
168
-
169
- begin
170
- raise_exception unless @exception_to_raise.nil?
171
- Kernel::throw(*@args_to_throw) unless @args_to_throw.empty?
172
-
173
- default_return_val = call_with_yield(&block) if !@args_to_yield.empty? || @eval_context
174
-
175
- if @consecutive
176
- call_implementation_consecutive(*args, &block)
177
- elsif @implementation
178
- call_implementation(*args, &block)
179
- else
180
- default_return_val
181
- end
182
- ensure
183
- @actual_received_count += 1
184
- end
185
- end
186
-
187
- # @private
188
- def raise_exception
189
- if !@exception_to_raise.respond_to?(:instance_method) ||
190
- @exception_to_raise.instance_method(:initialize).arity <= 0
191
- raise(@exception_to_raise)
192
- else
193
- raise ArgumentError.new(<<-MESSAGE)
194
- 'and_raise' can only accept an Exception class if an instance can be constructed with no arguments.
195
- #{@exception_to_raise.to_s}'s initialize method requires #{@exception_to_raise.instance_method(:initialize).arity} arguments, so you have to supply an instance instead.
196
- MESSAGE
197
- end
198
- end
199
-
200
- # @private
201
- def called_max_times?
202
- @expected_received_count != :any &&
203
- !@at_least &&
204
- @expected_received_count > 0 &&
205
- @actual_received_count >= @expected_received_count
206
- end
207
-
208
- # @private
209
- def matches_name_but_not_args(message, *args)
210
- @message == message and not @argument_list_matcher.args_match?(*args)
211
- end
212
-
213
- # @private
214
- def verify_messages_received
215
- generate_error unless expected_messages_received? || failed_fast?
216
- rescue RSpec::Mocks::MockExpectationError => error
217
- error.backtrace.insert(0, @expected_from)
218
- Kernel::raise error
219
- end
220
-
221
- # @private
222
- def expected_messages_received?
223
- ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
224
- end
225
-
226
- # @private
227
- def ignoring_args?
228
- @expected_received_count == :any
229
- end
230
-
231
- # @private
232
- def matches_at_least_count?
233
- @at_least && @actual_received_count >= @expected_received_count
234
- end
235
-
236
- # @private
237
- def matches_at_most_count?
238
- @at_most && @actual_received_count <= @expected_received_count
239
- end
240
-
241
- # @private
242
- def matches_exact_count?
243
- @expected_received_count == @actual_received_count
244
- end
245
-
246
- # @private
247
- def similar_messages
248
- @similar_messages ||= []
249
- end
250
-
251
- # @private
252
- def advise(*args)
253
- similar_messages << args
254
- end
255
-
256
- # @private
257
- def generate_error
258
- if similar_messages.empty?
259
- @error_generator.raise_expectation_error(@message, @expected_received_count, @actual_received_count, *expected_args)
260
- else
261
- @error_generator.raise_similar_message_args_error(self, *@similar_messages)
262
- end
263
- end
264
-
265
- def raise_out_of_order_error
266
- @error_generator.raise_out_of_order_error @message
267
- end
268
-
269
- # Constrains a stub or message expectation to invocations with specific
270
- # arguments.
271
- #
272
- # With a stub, if the message might be received with other args as well,
273
- # you should stub a default value first, and then stub or mock the same
274
- # message using `with` to constrain to specific arguments.
275
- #
276
- # A message expectation will fail if the message is received with different
277
- # arguments.
278
- #
279
- # @example
280
- #
281
- # cart.stub(:add) { :failure }
282
- # cart.stub(:add).with(Book.new(:isbn => 1934356379)) { :success }
283
- # cart.add(Book.new(:isbn => 1234567890))
284
- # # => :failure
285
- # cart.add(Book.new(:isbn => 1934356379))
286
- # # => :success
287
- #
288
- # cart.should_receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
289
- # cart.add(Book.new(:isbn => 1234567890))
290
- # # => failed expectation
291
- # cart.add(Book.new(:isbn => 1934356379))
292
- # # => passes
293
- def with(*args, &block)
294
- @implementation = block if block_given? unless args.empty?
295
- @argument_list_matcher = ArgumentListMatcher.new(*args, &block)
296
- self
297
- end
228
+ # @!group Constraining Receive Counts
298
229
 
299
230
  # Constrain a message expectation to be received a specific number of
300
231
  # times.
301
232
  #
233
+ # @return [MessageExpectation] self, to support further chaining.
302
234
  # @example
303
- #
304
- # dealer.should_recieve(:deal_card).exactly(10).times
235
+ # expect(dealer).to receive(:deal_card).exactly(10).times
305
236
  def exactly(n, &block)
306
- @implementation = block if block
237
+ raise_already_invoked_error_if_necessary(__method__)
238
+ self.inner_implementation_action = block
307
239
  set_expected_received_count :exactly, n
308
240
  self
309
241
  end
@@ -311,177 +243,579 @@ MESSAGE
311
243
  # Constrain a message expectation to be received at least a specific
312
244
  # number of times.
313
245
  #
246
+ # @return [MessageExpectation] self, to support further chaining.
314
247
  # @example
315
- #
316
- # dealer.should_recieve(:deal_card).at_least(9).times
248
+ # expect(dealer).to receive(:deal_card).at_least(9).times
317
249
  def at_least(n, &block)
318
- @implementation = block if block
250
+ raise_already_invoked_error_if_necessary(__method__)
319
251
  set_expected_received_count :at_least, n
252
+
253
+ if n == 0
254
+ raise "at_least(0) has been removed, use allow(...).to receive(:message) instead"
255
+ end
256
+
257
+ self.inner_implementation_action = block
258
+
320
259
  self
321
260
  end
322
261
 
323
262
  # Constrain a message expectation to be received at most a specific
324
263
  # number of times.
325
264
  #
265
+ # @return [MessageExpectation] self, to support further chaining.
326
266
  # @example
327
- #
328
- # dealer.should_recieve(:deal_card).at_most(10).times
267
+ # expect(dealer).to receive(:deal_card).at_most(10).times
329
268
  def at_most(n, &block)
330
- @implementation = block if block
269
+ raise_already_invoked_error_if_necessary(__method__)
270
+ self.inner_implementation_action = block
331
271
  set_expected_received_count :at_most, n
332
272
  self
333
273
  end
334
274
 
335
275
  # Syntactic sugar for `exactly`, `at_least` and `at_most`
336
276
  #
277
+ # @return [MessageExpectation] self, to support further chaining.
337
278
  # @example
338
- #
339
- # dealer.should_recieve(:deal_card).exactly(10).times
340
- # dealer.should_recieve(:deal_card).at_least(10).times
341
- # dealer.should_recieve(:deal_card).at_most(10).times
279
+ # expect(dealer).to receive(:deal_card).exactly(10).times
280
+ # expect(dealer).to receive(:deal_card).at_least(10).times
281
+ # expect(dealer).to receive(:deal_card).at_most(10).times
342
282
  def times(&block)
343
- @implementation = block if block
344
- self
345
- end
346
-
347
-
348
- # Allows an expected message to be received any number of times.
349
- def any_number_of_times(&block)
350
- @implementation = block if block
351
- @expected_received_count = :any
283
+ self.inner_implementation_action = block
352
284
  self
353
285
  end
286
+ alias time times
354
287
 
355
288
  # Expect a message not to be received at all.
356
289
  #
290
+ # @return [MessageExpectation] self, to support further chaining.
357
291
  # @example
358
- #
359
- # car.should_receive(:stop).never
292
+ # expect(car).to receive(:stop).never
360
293
  def never
294
+ error_generator.raise_double_negation_error("expect(obj)") if negative?
361
295
  @expected_received_count = 0
362
296
  self
363
297
  end
364
298
 
365
299
  # Expect a message to be received exactly one time.
366
300
  #
301
+ # @return [MessageExpectation] self, to support further chaining.
367
302
  # @example
368
- #
369
- # car.should_receive(:go).once
303
+ # expect(car).to receive(:go).once
370
304
  def once(&block)
371
- @implementation = block if block
305
+ self.inner_implementation_action = block
372
306
  set_expected_received_count :exactly, 1
373
307
  self
374
308
  end
375
309
 
376
310
  # Expect a message to be received exactly two times.
377
311
  #
312
+ # @return [MessageExpectation] self, to support further chaining.
378
313
  # @example
379
- #
380
- # car.should_receive(:go).twice
314
+ # expect(car).to receive(:go).twice
381
315
  def twice(&block)
382
- @implementation = block if block
316
+ self.inner_implementation_action = block
383
317
  set_expected_received_count :exactly, 2
384
318
  self
385
319
  end
386
320
 
387
- # Expect messages to be received in a specific order.
321
+ # Expect a message to be received exactly three times.
322
+ #
323
+ # @return [MessageExpectation] self, to support further chaining.
324
+ # @example
325
+ # expect(car).to receive(:go).thrice
326
+ def thrice(&block)
327
+ self.inner_implementation_action = block
328
+ set_expected_received_count :exactly, 3
329
+ self
330
+ end
331
+ # @!endgroup
332
+
333
+ # @!group Other Constraints
334
+
335
+ # Constrains a stub or message expectation to invocations with specific
336
+ # arguments.
337
+ #
338
+ # With a stub, if the message might be received with other args as well,
339
+ # you should stub a default value first, and then stub or mock the same
340
+ # message using `with` to constrain to specific arguments.
341
+ #
342
+ # A message expectation will fail if the message is received with different
343
+ # arguments.
388
344
  #
345
+ # @return [MessageExpectation] self, to support further chaining.
389
346
  # @example
347
+ # allow(cart).to receive(:add) { :failure }
348
+ # allow(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
349
+ # cart.add(Book.new(:isbn => 1234567890))
350
+ # # => :failure
351
+ # cart.add(Book.new(:isbn => 1934356379))
352
+ # # => :success
353
+ #
354
+ # expect(cart).to receive(:add).with(Book.new(:isbn => 1934356379)) { :success }
355
+ # cart.add(Book.new(:isbn => 1234567890))
356
+ # # => failed expectation
357
+ # cart.add(Book.new(:isbn => 1934356379))
358
+ # # => passes
359
+ def with(*args, &block)
360
+ raise_already_invoked_error_if_necessary(__method__)
361
+ if args.empty?
362
+ raise ArgumentError,
363
+ "`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
364
+ end
365
+
366
+ self.inner_implementation_action = block
367
+ @argument_list_matcher = ArgumentListMatcher.new(*args)
368
+ self
369
+ end
370
+ ruby2_keywords(:with) if respond_to?(:ruby2_keywords, true)
371
+
372
+ # Expect messages to be received in a specific order.
390
373
  #
391
- # api.should_receive(:prepare).ordered
392
- # api.should_receive(:run).ordered
393
- # api.should_receive(:finish).ordered
374
+ # @return [MessageExpectation] self, to support further chaining.
375
+ # @example
376
+ # expect(api).to receive(:prepare).ordered
377
+ # expect(api).to receive(:run).ordered
378
+ # expect(api).to receive(:finish).ordered
394
379
  def ordered(&block)
395
- @implementation = block if block
396
- @order_group.register(self)
380
+ if type == :stub
381
+ RSpec.warning(
382
+ "`allow(...).to receive(..).ordered` is not supported and will " \
383
+ "have no effect, use `and_return(*ordered_values)` instead."
384
+ )
385
+ end
386
+
387
+ self.inner_implementation_action = block
388
+ additional_expected_calls.times do
389
+ @order_group.register(self)
390
+ end
397
391
  @ordered = true
398
392
  self
399
393
  end
400
394
 
401
- # @private
402
- def negative_expectation_for?(message)
403
- return false
395
+ # @return [String] a nice representation of the message expectation
396
+ def to_s
397
+ args_description = error_generator.method_call_args_description(@argument_list_matcher.expected_args, "", "") { true }
398
+ args_description = "(#{args_description})" unless args_description.start_with?("(")
399
+ "#<#{self.class} #{error_generator.intro}.#{message}#{args_description}>"
404
400
  end
401
+ alias inspect to_s
405
402
 
406
403
  # @private
407
- def actual_received_count_matters?
408
- @at_least || @at_most || @exactly
404
+ # Contains the parts of `MessageExpectation` that aren't part of
405
+ # rspec-mocks' public API. The class is very big and could really use
406
+ # some collaborators it delegates to for this stuff but for now this was
407
+ # the simplest way to split the public from private stuff to make it
408
+ # easier to publish the docs for the APIs we want published.
409
+ # rubocop:disable Metrics/ModuleLength
410
+ module ImplementationDetails
411
+ attr_accessor :error_generator, :implementation
412
+ attr_reader :message
413
+ attr_reader :orig_object
414
+ attr_writer :expected_received_count, :expected_from, :argument_list_matcher
415
+ protected :expected_received_count=, :expected_from=, :error_generator=, :implementation=
416
+
417
+ # @private
418
+ attr_reader :type
419
+
420
+ # rubocop:disable Metrics/ParameterLists
421
+ def initialize(error_generator, expectation_ordering, expected_from, method_double,
422
+ type=:expectation, opts={}, &implementation_block)
423
+ @type = type
424
+ @error_generator = error_generator
425
+ @error_generator.opts = error_generator.opts.merge(opts)
426
+ @expected_from = expected_from
427
+ @method_double = method_double
428
+ @orig_object = @method_double.object
429
+ @message = @method_double.method_name
430
+ @actual_received_count = 0
431
+ @actual_received_count_write_mutex = Support::Mutex.new
432
+ @expected_received_count = type == :expectation ? 1 : :any
433
+ @argument_list_matcher = ArgumentListMatcher::MATCH_ALL
434
+ @order_group = expectation_ordering
435
+ @order_group.register(self) unless type == :stub
436
+ @expectation_type = type
437
+ @ordered = false
438
+ @at_least = @at_most = @exactly = nil
439
+
440
+ # Initialized to nil so that we don't allocate an array for every
441
+ # mock or stub. See also comment in `and_yield`.
442
+ @args_to_yield = nil
443
+ @eval_context = nil
444
+ @yield_receiver_to_implementation_block = false
445
+
446
+ @implementation = Implementation.new
447
+ self.inner_implementation_action = implementation_block
448
+ end
449
+ # rubocop:enable Metrics/ParameterLists
450
+
451
+ def expected_args
452
+ @argument_list_matcher.expected_args
453
+ end
454
+
455
+ def and_yield_receiver_to_implementation
456
+ @yield_receiver_to_implementation_block = true
457
+ self
458
+ end
459
+
460
+ def yield_receiver_to_implementation_block?
461
+ @yield_receiver_to_implementation_block
462
+ end
463
+
464
+ def matches?(message, *args)
465
+ @message == message && @argument_list_matcher.args_match?(*args)
466
+ end
467
+ ruby2_keywords :matches? if respond_to?(:ruby2_keywords, true)
468
+
469
+ def safe_invoke(parent_stub, *args, &block)
470
+ invoke_incrementing_actual_calls_by(1, false, parent_stub, *args, &block)
471
+ end
472
+ ruby2_keywords :safe_invoke if respond_to?(:ruby2_keywords, true)
473
+
474
+ def invoke(parent_stub, *args, &block)
475
+ invoke_incrementing_actual_calls_by(1, true, parent_stub, *args, &block)
476
+ end
477
+ ruby2_keywords :invoke if respond_to?(:ruby2_keywords, true)
478
+
479
+ def invoke_without_incrementing_received_count(parent_stub, *args, &block)
480
+ invoke_incrementing_actual_calls_by(0, true, parent_stub, *args, &block)
481
+ end
482
+ ruby2_keywords :invoke_without_incrementing_received_count if respond_to?(:ruby2_keywords, true)
483
+
484
+ def negative?
485
+ @expected_received_count == 0 && !@at_least
486
+ end
487
+
488
+ def called_max_times?
489
+ @expected_received_count != :any &&
490
+ !@at_least &&
491
+ @expected_received_count > 0 &&
492
+ @actual_received_count >= @expected_received_count
493
+ end
494
+
495
+ def matches_name_but_not_args(message, *args)
496
+ @message == message && !@argument_list_matcher.args_match?(*args)
497
+ end
498
+
499
+ def verify_messages_received
500
+ return if expected_messages_received?
501
+ generate_error
502
+ end
503
+
504
+ def expected_messages_received?
505
+ ignoring_args? || matches_exact_count? || matches_at_least_count? || matches_at_most_count?
506
+ end
507
+
508
+ def ensure_expected_ordering_received!
509
+ @order_group.verify_invocation_order(self) if @ordered
510
+ true
511
+ end
512
+
513
+ def ignoring_args?
514
+ @expected_received_count == :any
515
+ end
516
+
517
+ def matches_at_least_count?
518
+ @at_least && @actual_received_count >= @expected_received_count
519
+ end
520
+
521
+ def matches_at_most_count?
522
+ @at_most && @actual_received_count <= @expected_received_count
523
+ end
524
+
525
+ def matches_exact_count?
526
+ @expected_received_count == @actual_received_count
527
+ end
528
+
529
+ def similar_messages
530
+ @similar_messages ||= []
531
+ end
532
+
533
+ def advise(*args)
534
+ similar_messages << args
535
+ end
536
+
537
+ def unadvise(args)
538
+ similar_messages.delete_if { |message| args.include?(message) }
539
+ end
540
+
541
+ def generate_error
542
+ if similar_messages.empty?
543
+ @error_generator.raise_expectation_error(
544
+ @message, @expected_received_count, @argument_list_matcher,
545
+ @actual_received_count, expectation_count_type, expected_args,
546
+ @expected_from, exception_source_id
547
+ )
548
+ else
549
+ @error_generator.raise_similar_message_args_error(
550
+ self, @similar_messages, @expected_from
551
+ )
552
+ end
553
+ end
554
+
555
+ def raise_unexpected_message_args_error(args_for_multiple_calls)
556
+ @error_generator.raise_unexpected_message_args_error(self, args_for_multiple_calls, exception_source_id)
557
+ end
558
+
559
+ def expectation_count_type
560
+ return :at_least if @at_least
561
+ return :at_most if @at_most
562
+ nil
563
+ end
564
+
565
+ def description_for(verb)
566
+ @error_generator.describe_expectation(
567
+ verb, @message, @expected_received_count,
568
+ @actual_received_count, expected_args
569
+ )
570
+ end
571
+
572
+ def raise_out_of_order_error
573
+ @error_generator.raise_out_of_order_error @message
574
+ end
575
+
576
+ def additional_expected_calls
577
+ return 0 if @expectation_type == :stub || !@exactly
578
+ @expected_received_count - 1
579
+ end
580
+
581
+ def ordered?
582
+ @ordered
583
+ end
584
+
585
+ def negative_expectation_for?(message)
586
+ @message == message && negative?
587
+ end
588
+
589
+ def actual_received_count_matters?
590
+ @at_least || @at_most || @exactly
591
+ end
592
+
593
+ def increase_actual_received_count!
594
+ @actual_received_count_write_mutex.synchronize do
595
+ @actual_received_count += 1
596
+ end
597
+ end
598
+
599
+ private
600
+
601
+ def exception_source_id
602
+ @exception_source_id ||= "#{self.class.name} #{__id__}"
603
+ end
604
+
605
+ def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block)
606
+ args.unshift(orig_object) if yield_receiver_to_implementation_block?
607
+
608
+ if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count))
609
+ # args are the args we actually received, @argument_list_matcher is the
610
+ # list of args we were expecting
611
+ @error_generator.raise_expectation_error(
612
+ @message, @expected_received_count,
613
+ @argument_list_matcher,
614
+ @actual_received_count + increment,
615
+ expectation_count_type, args, nil, exception_source_id
616
+ )
617
+ end
618
+
619
+ @order_group.handle_order_constraint self
620
+
621
+ if implementation.present?
622
+ implementation.call(*args, &block)
623
+ elsif parent_stub
624
+ parent_stub.invoke(nil, *args, &block)
625
+ end
626
+ ensure
627
+ @actual_received_count_write_mutex.synchronize do
628
+ @actual_received_count += increment
629
+ end
630
+ end
631
+ ruby2_keywords :invoke_incrementing_actual_calls_by if respond_to?(:ruby2_keywords, true)
632
+
633
+ def has_been_invoked?
634
+ @actual_received_count > 0
635
+ end
636
+
637
+ def raise_already_invoked_error_if_necessary(calling_customization)
638
+ return unless has_been_invoked?
639
+
640
+ error_generator.raise_already_invoked_error(message, calling_customization)
641
+ end
642
+
643
+ def set_expected_received_count(relativity, n)
644
+ raise "`count` is not supported with negative message expectations" if negative?
645
+ @at_least = (relativity == :at_least)
646
+ @at_most = (relativity == :at_most)
647
+ @exactly = (relativity == :exactly)
648
+ @expected_received_count = case n
649
+ when Numeric then n
650
+ when :once then 1
651
+ when :twice then 2
652
+ when :thrice then 3
653
+ end
654
+ end
655
+
656
+ def initial_implementation_action=(action)
657
+ implementation.initial_action = action
658
+ end
659
+
660
+ def inner_implementation_action=(action)
661
+ return unless action
662
+ warn_about_stub_override if implementation.inner_action
663
+ implementation.inner_action = action
664
+ end
665
+
666
+ def terminal_implementation_action=(action)
667
+ implementation.terminal_action = action
668
+ end
669
+
670
+ def warn_about_stub_override
671
+ RSpec.warning(
672
+ "You're overriding a previous stub implementation of `#{@message}`. " \
673
+ "Called from #{CallerFilter.first_non_rspec_line}."
674
+ )
675
+ end
676
+
677
+ def wrap_original(method_name, &block)
678
+ if RSpec::Mocks::TestDouble === @method_double.object
679
+ @error_generator.raise_only_valid_on_a_partial_double(method_name)
680
+ else
681
+ warn_about_stub_override if implementation.inner_action
682
+ @implementation = AndWrapOriginalImplementation.new(@method_double.original_implementation_callable, block)
683
+ @yield_receiver_to_implementation_block = false
684
+ end
685
+
686
+ nil
687
+ end
409
688
  end
689
+ # rubocop:enable Metrics/ModuleLength
410
690
 
411
- # @private
412
- def increase_actual_received_count!
413
- @actual_received_count += 1
691
+ include ImplementationDetails
692
+ end
693
+
694
+ # Handles the implementation of an `and_yield` declaration.
695
+ # @private
696
+ class AndYieldImplementation
697
+ def initialize(args_to_yield, eval_context, error_generator)
698
+ @args_to_yield = args_to_yield
699
+ @eval_context = eval_context
700
+ @error_generator = error_generator
414
701
  end
415
702
 
416
- protected
703
+ def call(*_args_to_ignore, &block)
704
+ return if @args_to_yield.empty? && @eval_context.nil?
417
705
 
418
- def call_with_yield(&block)
419
706
  @error_generator.raise_missing_block_error @args_to_yield unless block
420
707
  value = nil
708
+ block_signature = Support::BlockSignature.new(block)
709
+
421
710
  @args_to_yield.each do |args|
422
- if block.arity > -1 && args.length != block.arity
423
- @error_generator.raise_wrong_arity_error args, block.arity
711
+ unless Support::StrictSignatureVerifier.new(block_signature, args).valid?
712
+ @error_generator.raise_wrong_arity_error(args, block_signature)
424
713
  end
425
- value = @eval_context ? @eval_context.instance_exec(*args, &block) : block.call(*args)
714
+
715
+ value = @eval_context ? @eval_context.instance_exec(*args, &block) : yield(*args)
426
716
  end
427
717
  value
428
718
  end
719
+ end
429
720
 
430
- def call_implementation_consecutive(*args, &block)
431
- @value ||= call_implementation(*args, &block)
432
- @value[[@actual_received_count, @value.size-1].min]
721
+ # Handles the implementation of an `and_return` implementation.
722
+ # @private
723
+ class AndReturnImplementation
724
+ def initialize(values_to_return)
725
+ @values_to_return = values_to_return
433
726
  end
434
727
 
435
- def call_implementation(*args, &block)
436
- @implementation.arity == 0 ? @implementation.call(&block) : @implementation.call(*args, &block)
728
+ def call(*_args_to_ignore, &_block)
729
+ if @values_to_return.size > 1
730
+ @values_to_return.shift
731
+ else
732
+ @values_to_return.first
733
+ end
437
734
  end
735
+ end
438
736
 
439
- def clone_args_to_yield(*args)
440
- @args_to_yield = args.clone
441
- @args_to_yield_were_cloned = true
737
+ # Handles the implementation of an `and_invoke` implementation.
738
+ # @private
739
+ class AndInvokeImplementation
740
+ def initialize(procs_to_invoke)
741
+ @procs_to_invoke = procs_to_invoke
442
742
  end
443
743
 
444
- def failed_fast?
445
- @failed_fast
744
+ def call(*args, &block)
745
+ proc = if @procs_to_invoke.size > 1
746
+ @procs_to_invoke.shift
747
+ else
748
+ @procs_to_invoke.first
749
+ end
750
+
751
+ proc.call(*args, &block)
446
752
  end
753
+ end
447
754
 
448
- def set_expected_received_count(relativity, n)
449
- @at_least = (relativity == :at_least)
450
- @at_most = (relativity == :at_most)
451
- @exactly = (relativity == :exactly)
452
- @expected_received_count = case n
453
- when Numeric then n
454
- when :once then 1
455
- when :twice then 2
456
- end
755
+ # Represents a configured implementation. Takes into account
756
+ # any number of sub-implementations.
757
+ # @private
758
+ class Implementation
759
+ attr_accessor :initial_action, :inner_action, :terminal_action
760
+
761
+ def call(*args, &block)
762
+ actions.map do |action|
763
+ action.call(*args, &block)
764
+ end.last
457
765
  end
766
+ ruby2_keywords :call if respond_to?(:ruby2_keywords, true)
458
767
 
459
- def clear_actual_received_count!
460
- @actual_received_count = 0
768
+ def present?
769
+ actions.any?
461
770
  end
462
771
 
463
- private
772
+ private
464
773
 
465
- def build_implementation(values)
466
- value = values.size == 1 ? values.first : values
467
- lambda { value }
774
+ def actions
775
+ [initial_action, inner_action, terminal_action].compact
468
776
  end
469
777
  end
470
778
 
779
+ # Represents an `and_call_original` implementation.
471
780
  # @private
472
- class NegativeMessageExpectation < MessageExpectation
473
- # @private
474
- def initialize(error_generator, expectation_ordering, expected_from, message, &implementation)
475
- super(error_generator, expectation_ordering, expected_from, message, 0, {}, &implementation)
781
+ class AndWrapOriginalImplementation
782
+ def initialize(method, block)
783
+ @method = method
784
+ @block = block
476
785
  end
477
786
 
478
- def and_return(*)
479
- # no-op
787
+ CannotModifyFurtherError = Class.new(StandardError)
788
+
789
+ def initial_action=(_value)
790
+ raise cannot_modify_further_error
480
791
  end
481
792
 
482
- # @private
483
- def negative_expectation_for?(message)
484
- return @message == message
793
+ def inner_action=(_value)
794
+ raise cannot_modify_further_error
795
+ end
796
+
797
+ def terminal_action=(_value)
798
+ raise cannot_modify_further_error
799
+ end
800
+
801
+ def present?
802
+ true
803
+ end
804
+
805
+ def inner_action
806
+ true
807
+ end
808
+
809
+ def call(*args, &block)
810
+ @block.call(@method, *args, &block)
811
+ end
812
+ ruby2_keywords :call if respond_to?(:ruby2_keywords, true)
813
+
814
+ private
815
+
816
+ def cannot_modify_further_error
817
+ CannotModifyFurtherError.new "This method has already been configured " \
818
+ "to call the original implementation, and cannot be modified further."
485
819
  end
486
820
  end
487
821
  end