rspec-mocks 3.0.4 → 3.1.0
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/Changelog.md +26 -0
- data/README.md +29 -15
- data/lib/rspec/mocks/any_instance/chain.rb +2 -2
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +2 -3
- data/lib/rspec/mocks/any_instance/message_chains.rb +8 -7
- data/lib/rspec/mocks/any_instance/proxy.rb +2 -2
- data/lib/rspec/mocks/any_instance/recorder.rb +27 -25
- data/lib/rspec/mocks/any_instance/stub_chain.rb +3 -5
- data/lib/rspec/mocks/argument_list_matcher.rb +4 -4
- data/lib/rspec/mocks/argument_matchers.rb +23 -5
- data/lib/rspec/mocks/configuration.rb +3 -10
- data/lib/rspec/mocks/error_generator.rb +33 -27
- data/lib/rspec/mocks/example_methods.rb +74 -6
- data/lib/rspec/mocks/instance_method_stasher.rb +5 -5
- data/lib/rspec/mocks/matchers/have_received.rb +7 -8
- data/lib/rspec/mocks/matchers/receive.rb +8 -8
- data/lib/rspec/mocks/matchers/receive_message_chain.rb +4 -5
- data/lib/rspec/mocks/matchers/receive_messages.rb +6 -7
- data/lib/rspec/mocks/message_chain.rb +5 -5
- data/lib/rspec/mocks/message_expectation.rb +56 -28
- data/lib/rspec/mocks/method_double.rb +15 -12
- data/lib/rspec/mocks/method_reference.rb +8 -7
- data/lib/rspec/mocks/mutate_const.rb +26 -100
- data/lib/rspec/mocks/object_reference.rb +12 -13
- data/lib/rspec/mocks/order_group.rb +4 -5
- data/lib/rspec/mocks/proxy.rb +31 -25
- data/lib/rspec/mocks/space.rb +24 -24
- data/lib/rspec/mocks/syntax.rb +8 -8
- data/lib/rspec/mocks/targets.rb +6 -6
- data/lib/rspec/mocks/test_double.rb +3 -3
- data/lib/rspec/mocks/verifying_double.rb +10 -12
- data/lib/rspec/mocks/verifying_message_expecation.rb +15 -13
- data/lib/rspec/mocks/verifying_proxy.rb +11 -15
- data/lib/rspec/mocks/version.rb +1 -1
- metadata +5 -5
- metadata.gz.sig +0 -0
@@ -40,29 +40,38 @@ module RSpec
|
|
40
40
|
def raise_unexpected_message_args_error(expectation, *args)
|
41
41
|
expected_args = format_args(*expectation.expected_args)
|
42
42
|
actual_args = format_received_args(*args)
|
43
|
-
__raise "#{intro} received #{expectation.message.inspect} with
|
43
|
+
__raise "#{intro} received #{expectation.message.inspect} with " \
|
44
|
+
"unexpected arguments\n expected: #{expected_args}\n" \
|
45
|
+
" got: #{actual_args}"
|
44
46
|
end
|
45
47
|
|
46
48
|
# @private
|
47
49
|
def raise_missing_default_stub_error(expectation, *args)
|
48
50
|
expected_args = format_args(*expectation.expected_args)
|
49
51
|
actual_args = format_received_args(*args)
|
50
|
-
__raise "#{intro} received #{expectation.message.inspect} with
|
52
|
+
__raise "#{intro} received #{expectation.message.inspect} with " \
|
53
|
+
"unexpected arguments\n expected: #{expected_args}\n" \
|
54
|
+
" got: #{actual_args}\n Please stub a default value " \
|
55
|
+
"first if message might be received with other args as well. \n"
|
51
56
|
end
|
52
57
|
|
53
58
|
# @private
|
54
59
|
def raise_similar_message_args_error(expectation, *args_for_multiple_calls)
|
55
60
|
expected_args = format_args(*expectation.expected_args)
|
56
|
-
actual_args = args_for_multiple_calls.
|
57
|
-
__raise "#{intro} received #{expectation.message.inspect} with
|
61
|
+
actual_args = args_for_multiple_calls.map { |a| format_received_args(*a) }.join(", ")
|
62
|
+
__raise "#{intro} received #{expectation.message.inspect} with " \
|
63
|
+
"unexpected arguments\n expected: #{expected_args}\n" \
|
64
|
+
" got: #{actual_args}"
|
58
65
|
end
|
59
66
|
|
67
|
+
# rubocop:disable Style/ParameterLists
|
60
68
|
# @private
|
61
69
|
def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, *args)
|
62
70
|
expected_part = expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher)
|
63
71
|
received_part = received_part_of_expectation_error(actual_received_count, *args)
|
64
72
|
__raise "(#{intro}).#{message}#{format_args(*args)}\n #{expected_part}\n #{received_part}"
|
65
73
|
end
|
74
|
+
# rubocop:enable Style/ParameterLists
|
66
75
|
|
67
76
|
# @private
|
68
77
|
def raise_unimplemented_error(doubled_module, method_name)
|
@@ -87,10 +96,10 @@ module RSpec
|
|
87
96
|
# @private
|
88
97
|
def raise_expired_test_double_error
|
89
98
|
raise ExpiredTestDoubleError,
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
99
|
+
"#{intro} was originally created in one example but has leaked into " \
|
100
|
+
"another example and can no longer be used. rspec-mocks' doubles are " \
|
101
|
+
"designed to only last for one example, and you need to create a new " \
|
102
|
+
"one in each example you wish to use it for."
|
94
103
|
end
|
95
104
|
|
96
105
|
# @private
|
@@ -128,15 +137,13 @@ module RSpec
|
|
128
137
|
# @private
|
129
138
|
def method_call_args_description(args)
|
130
139
|
case args.first
|
131
|
-
|
132
|
-
|
133
|
-
when ArgumentMatchers::NoArgsMatcher
|
134
|
-
return " with no arguments"
|
140
|
+
when ArgumentMatchers::AnyArgsMatcher then " with any arguments"
|
141
|
+
when ArgumentMatchers::NoArgsMatcher then " with no arguments"
|
135
142
|
end
|
136
143
|
end
|
137
144
|
|
138
145
|
# @private
|
139
|
-
def describe_expectation(message, expected_received_count,
|
146
|
+
def describe_expectation(message, expected_received_count, _actual_received_count, *args)
|
140
147
|
"have received #{message}#{format_args(*args)} #{count_message(expected_received_count)}"
|
141
148
|
end
|
142
149
|
|
@@ -162,30 +169,30 @@ module RSpec
|
|
162
169
|
|
163
170
|
# @private
|
164
171
|
def raise_only_valid_on_a_partial_double(method)
|
165
|
-
__raise "#{intro} is a pure test double. `#{method}` is only "
|
172
|
+
__raise "#{intro} is a pure test double. `#{method}` is only " \
|
166
173
|
"available on a partial double."
|
167
174
|
end
|
168
175
|
|
169
176
|
# @private
|
170
177
|
def raise_expectation_on_unstubbed_method(method)
|
171
|
-
__raise "#{intro} expected to have received #{method}, but that "
|
172
|
-
"method has not been stubbed."
|
178
|
+
__raise "#{intro} expected to have received #{method}, but that " \
|
179
|
+
"object is not a spy or method has not been stubbed."
|
173
180
|
end
|
174
181
|
|
175
182
|
# @private
|
176
183
|
def raise_expectation_on_mocked_method(method)
|
177
|
-
__raise "#{intro} expected to have received #{method}, but that "
|
178
|
-
"method has been mocked instead of stubbed."
|
184
|
+
__raise "#{intro} expected to have received #{method}, but that " \
|
185
|
+
"method has been mocked instead of stubbed or spied."
|
179
186
|
end
|
180
187
|
|
181
188
|
def self.raise_double_negation_error(wrapped_expression)
|
182
|
-
raise "Isn't life confusing enough? You've already set a "
|
183
|
-
"negative message expectation and now you are trying to "
|
184
|
-
"negate it again with `never`. What does an expression like "
|
189
|
+
raise "Isn't life confusing enough? You've already set a " \
|
190
|
+
"negative message expectation and now you are trying to " \
|
191
|
+
"negate it again with `never`. What does an expression like " \
|
185
192
|
"`#{wrapped_expression}.not_to receive(:msg).never` even mean?"
|
186
193
|
end
|
187
194
|
|
188
|
-
|
195
|
+
private
|
189
196
|
|
190
197
|
def intro
|
191
198
|
if @name
|
@@ -203,7 +210,7 @@ module RSpec
|
|
203
210
|
|
204
211
|
def __raise(message)
|
205
212
|
message = opts[:message] unless opts[:message].nil?
|
206
|
-
Kernel
|
213
|
+
Kernel.raise(RSpec::Mocks::MockExpectationError, message)
|
207
214
|
end
|
208
215
|
|
209
216
|
def arg_message(*args)
|
@@ -215,7 +222,7 @@ module RSpec
|
|
215
222
|
end
|
216
223
|
|
217
224
|
def arg_list(*args)
|
218
|
-
args.
|
225
|
+
args.map { |arg| arg_has_valid_description(arg) ? arg.description : arg.inspect }.join(", ")
|
219
226
|
end
|
220
227
|
|
221
228
|
def arg_has_valid_description(arg)
|
@@ -229,19 +236,18 @@ module RSpec
|
|
229
236
|
end
|
230
237
|
|
231
238
|
def received_arg_list(*args)
|
232
|
-
args.
|
239
|
+
args.map(&:inspect).join(", ")
|
233
240
|
end
|
234
241
|
|
235
242
|
def count_message(count, expectation_count_type=nil)
|
236
243
|
return "at least #{times(count.abs)}" if count < 0 || expectation_count_type == :at_least
|
237
244
|
return "at most #{times(count)}" if expectation_count_type == :at_most
|
238
|
-
|
245
|
+
times(count)
|
239
246
|
end
|
240
247
|
|
241
248
|
def times(count)
|
242
249
|
"#{count} time#{count == 1 ? '' : 's'}"
|
243
250
|
end
|
244
|
-
|
245
251
|
end
|
246
252
|
end
|
247
253
|
end
|
@@ -84,6 +84,73 @@ module RSpec
|
|
84
84
|
ExampleMethods.declare_verifying_double(ObjectVerifyingDouble, ref, *args)
|
85
85
|
end
|
86
86
|
|
87
|
+
# @overload spy()
|
88
|
+
# @overload spy(name)
|
89
|
+
# @param name [String/Symbol] used to clarify intent
|
90
|
+
# @overload spy(stubs)
|
91
|
+
# @param stubs (Hash) hash of message/return-value pairs
|
92
|
+
# @overload spy(name, stubs)
|
93
|
+
# @param name [String/Symbol] used to clarify intent
|
94
|
+
# @param stubs (Hash) hash of message/return-value pairs
|
95
|
+
# @return (Double)
|
96
|
+
#
|
97
|
+
# Constructs a test double that is optimized for use with
|
98
|
+
# `have_received`. With a normal double one has to stub methods in order
|
99
|
+
# to be able to spy them. A spy automatically spies on all methods.
|
100
|
+
def spy(*args)
|
101
|
+
double(*args).as_null_object
|
102
|
+
end
|
103
|
+
|
104
|
+
# @overload instance_spy(doubled_class)
|
105
|
+
# @param doubled_class [String, Class]
|
106
|
+
# @overload instance_spy(doubled_class, stubs)
|
107
|
+
# @param doubled_class [String, Class]
|
108
|
+
# @param stubs [Hash] hash of message/return-value pairs
|
109
|
+
# @return InstanceVerifyingDouble
|
110
|
+
#
|
111
|
+
# Constructs a test double that is optimized for use with `have_received`
|
112
|
+
# against a specific class. If the given class name has been loaded, only
|
113
|
+
# instance methods defined on the class are allowed to be stubbed. With
|
114
|
+
# a normal double one has to stub methods in order to be able to spy
|
115
|
+
# them. An instance_spy automatically spies on all instance methods to
|
116
|
+
# which the class responds.
|
117
|
+
def instance_spy(*args)
|
118
|
+
instance_double(*args).as_null_object
|
119
|
+
end
|
120
|
+
|
121
|
+
# @overload object_spy(object_or_name)
|
122
|
+
# @param object_or_name [String, Object]
|
123
|
+
# @overload object_spy(object_or_name, stubs)
|
124
|
+
# @param object_or_name [String, Object]
|
125
|
+
# @param stubs [Hash] hash of message/return-value pairs
|
126
|
+
# @return ObjectVerifyingDouble
|
127
|
+
#
|
128
|
+
# Constructs a test double that is optimized for use with `have_received`
|
129
|
+
# against a specific object. Only instance methods defined on the object
|
130
|
+
# are allowed to be stubbed. With a normal double one has to stub
|
131
|
+
# methods in order to be able to spy them. An object_spy automatically
|
132
|
+
# spies on all methods to which the object responds.
|
133
|
+
def object_spy(*args)
|
134
|
+
object_double(*args).as_null_object
|
135
|
+
end
|
136
|
+
|
137
|
+
# @overload class_spy(doubled_class)
|
138
|
+
# @param doubled_class [String, Module]
|
139
|
+
# @overload class_spy(doubled_class, stubs)
|
140
|
+
# @param doubled_class [String, Module]
|
141
|
+
# @param stubs [Hash] hash of message/return-value pairs
|
142
|
+
# @return ClassVerifyingDouble
|
143
|
+
#
|
144
|
+
# Constructs a test double that is optimized for use with `have_received`
|
145
|
+
# against a specific class. If the given class name has been loaded,
|
146
|
+
# only class methods defined on the class are allowed to be stubbed.
|
147
|
+
# With a normal double one has to stub methods in order to be able to spy
|
148
|
+
# them. An class_spy automatically spies on all class methods to which the
|
149
|
+
# class responds.
|
150
|
+
def class_spy(*args)
|
151
|
+
class_double(*args).as_null_object
|
152
|
+
end
|
153
|
+
|
87
154
|
# Disables warning messages about expectations being set on nil.
|
88
155
|
#
|
89
156
|
# By default warning messages are issued when expectations are set on
|
@@ -130,7 +197,7 @@ module RSpec
|
|
130
197
|
# stub_const("CardDeck", Class.new, :transfer_nested_constants => [:SUITS])
|
131
198
|
# CardDeck::SUITS # => our suits array
|
132
199
|
# CardDeck::NUM_CARDS # => uninitialized constant error
|
133
|
-
def stub_const(constant_name, value, options
|
200
|
+
def stub_const(constant_name, value, options={})
|
134
201
|
ConstantMutator.stub(constant_name, value, options)
|
135
202
|
end
|
136
203
|
|
@@ -151,8 +218,9 @@ module RSpec
|
|
151
218
|
end
|
152
219
|
|
153
220
|
# Verifies that the given object received the expected message during the
|
154
|
-
# course of the test.
|
155
|
-
#
|
221
|
+
# course of the test. On a spy objects or as null object doubles this
|
222
|
+
# works for any method, on other objects the method must have
|
223
|
+
# been stubbed beforehand in order for messages to be verified.
|
156
224
|
#
|
157
225
|
# Stubbing and verifying messages received in this way implements the
|
158
226
|
# Test Spy pattern.
|
@@ -288,9 +356,9 @@ module RSpec
|
|
288
356
|
!ref.defined?
|
289
357
|
|
290
358
|
raise VerifyingDoubleNotDefinedError,
|
291
|
-
|
292
|
-
|
293
|
-
|
359
|
+
"#{ref.description} is not a defined constant. " \
|
360
|
+
"Perhaps you misspelt it? " \
|
361
|
+
"Disable check with verify_doubled_constant_names configuration option."
|
294
362
|
end
|
295
363
|
|
296
364
|
declare_double(type, ref, *args)
|
@@ -53,7 +53,7 @@ module RSpec
|
|
53
53
|
|
54
54
|
# @private
|
55
55
|
def stash
|
56
|
-
return
|
56
|
+
return unless method_defined_directly_on_klass?
|
57
57
|
@original_method ||= ::RSpec::Support.method_handle_for(@object, @method)
|
58
58
|
end
|
59
59
|
|
@@ -80,9 +80,9 @@ module RSpec
|
|
80
80
|
yield
|
81
81
|
rescue TypeError
|
82
82
|
RSpec.warn_with(
|
83
|
-
"RSpec failed to properly restore a partial double (#{@object.inspect}) "
|
84
|
-
"to its original state due to a known bug in MRI 2.0.0-p195 & p247 "
|
85
|
-
"(https://bugs.ruby-lang.org/issues/8686). This object may remain "
|
83
|
+
"RSpec failed to properly restore a partial double (#{@object.inspect}) " \
|
84
|
+
"to its original state due to a known bug in MRI 2.0.0-p195 & p247 " \
|
85
|
+
"(https://bugs.ruby-lang.org/issues/8686). This object may remain " \
|
86
86
|
"screwed up for the rest of this process. Please upgrade to 2.0.0-p353 or above.",
|
87
87
|
:call_site => nil, :use_spec_location_as_call_site => true
|
88
88
|
)
|
@@ -102,7 +102,7 @@ module RSpec
|
|
102
102
|
end
|
103
103
|
|
104
104
|
# @private
|
105
|
-
def method_defined_on_klass?(klass
|
105
|
+
def method_defined_on_klass?(klass=@klass)
|
106
106
|
MethodReference.method_defined_at_any_visibility?(klass, @method)
|
107
107
|
end
|
108
108
|
|
@@ -3,9 +3,9 @@ module RSpec
|
|
3
3
|
module Matchers
|
4
4
|
# @private
|
5
5
|
class HaveReceived
|
6
|
-
COUNT_CONSTRAINTS = %w
|
7
|
-
ARGS_CONSTRAINTS = %w
|
8
|
-
CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w
|
6
|
+
COUNT_CONSTRAINTS = %w[exactly at_least at_most times once twice thrice]
|
7
|
+
ARGS_CONSTRAINTS = %w[with]
|
8
|
+
CONSTRAINTS = COUNT_CONSTRAINTS + ARGS_CONSTRAINTS + %w[ordered]
|
9
9
|
|
10
10
|
def initialize(method_name, &block)
|
11
11
|
@method_name = method_name
|
@@ -71,14 +71,13 @@ module RSpec
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def ensure_count_unconstrained
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
74
|
+
return unless count_constraint
|
75
|
+
raise RSpec::Mocks::MockExpectationError,
|
76
|
+
"can't use #{count_constraint} when negative"
|
78
77
|
end
|
79
78
|
|
80
79
|
def count_constraint
|
81
|
-
@constraints.map(&:first).
|
80
|
+
@constraints.map(&:first).find do |constraint|
|
82
81
|
COUNT_CONSTRAINTS.include?(constraint)
|
83
82
|
end
|
84
83
|
end
|
@@ -61,14 +61,14 @@ module RSpec
|
|
61
61
|
private
|
62
62
|
|
63
63
|
def warn_if_any_instance(expression, subject)
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
)
|
71
|
-
|
64
|
+
return unless AnyInstance::Proxy === subject
|
65
|
+
|
66
|
+
RSpec.warning(
|
67
|
+
"`#{expression}(#{subject.klass}.any_instance).to` " \
|
68
|
+
"is probably not what you meant, it does not operate on " \
|
69
|
+
"any instance of `#{subject.klass}`. " \
|
70
|
+
"Use `#{expression}_any_instance_of(#{subject.klass}).to` instead."
|
71
|
+
)
|
72
72
|
end
|
73
73
|
|
74
74
|
def setup_mock_proxy_method_substitute(subject, method, block)
|
@@ -44,11 +44,10 @@ module RSpec
|
|
44
44
|
replay_customizations(chain)
|
45
45
|
end
|
46
46
|
|
47
|
-
def setup_negative_expectation(*
|
48
|
-
raise NegationUnsupportedError
|
49
|
-
|
50
|
-
|
51
|
-
)
|
47
|
+
def setup_negative_expectation(*_args)
|
48
|
+
raise NegationUnsupportedError,
|
49
|
+
"`expect(...).not_to receive_message_chain` is not supported " \
|
50
|
+
"since it doesn't really make sense. What would it even mean?"
|
52
51
|
end
|
53
52
|
|
54
53
|
alias matches? setup_expectation
|
@@ -3,7 +3,6 @@ module RSpec
|
|
3
3
|
module Matchers
|
4
4
|
# @private
|
5
5
|
class ReceiveMessages
|
6
|
-
|
7
6
|
def initialize(message_return_value_hash)
|
8
7
|
@message_return_value_hash = message_return_value_hash
|
9
8
|
@backtrace_line = CallerFilter.first_non_rspec_line
|
@@ -15,29 +14,29 @@ module RSpec
|
|
15
14
|
|
16
15
|
def setup_expectation(subject)
|
17
16
|
warn_about_block if block_given?
|
18
|
-
each_message_on(
|
17
|
+
each_message_on(proxy_on(subject)) do |host, message, return_value|
|
19
18
|
host.add_simple_expectation(message, return_value, @backtrace_line)
|
20
19
|
end
|
21
20
|
end
|
22
21
|
alias matches? setup_expectation
|
23
22
|
|
24
|
-
def setup_negative_expectation(
|
23
|
+
def setup_negative_expectation(_subject)
|
25
24
|
raise NegationUnsupportedError,
|
26
|
-
|
27
|
-
|
25
|
+
"`expect(...).to_not receive_messages` is not supported since it " \
|
26
|
+
"doesn't really make sense. What would it even mean?"
|
28
27
|
end
|
29
28
|
alias does_not_match? setup_negative_expectation
|
30
29
|
|
31
30
|
def setup_allowance(subject)
|
32
31
|
warn_about_block if block_given?
|
33
|
-
each_message_on(
|
32
|
+
each_message_on(proxy_on(subject)) do |host, message, return_value|
|
34
33
|
host.add_simple_stub(message, return_value)
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
38
37
|
def setup_any_instance_expectation(subject)
|
39
38
|
warn_about_block if block_given?
|
40
|
-
each_message_on(
|
39
|
+
each_message_on(any_instance_of(subject)) do |host, message, return_value|
|
41
40
|
host.should_receive(message).and_return(return_value)
|
42
41
|
end
|
43
42
|
end
|
@@ -12,10 +12,10 @@ module RSpec
|
|
12
12
|
# @api private
|
13
13
|
def setup_chain
|
14
14
|
if chain.length > 1
|
15
|
-
if matching_stub = find_matching_stub
|
15
|
+
if (matching_stub = find_matching_stub)
|
16
16
|
chain.shift
|
17
17
|
chain_on(matching_stub.invoke(nil), *chain, &@block)
|
18
|
-
elsif matching_expectation = find_matching_expectation
|
18
|
+
elsif (matching_expectation = find_matching_expectation)
|
19
19
|
chain.shift
|
20
20
|
chain_on(matching_expectation.invoke_without_incrementing_received_count(nil), *chain, &@block)
|
21
21
|
else
|
@@ -30,8 +30,8 @@ module RSpec
|
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
def expectation(
|
34
|
-
raise NotImplementedError
|
33
|
+
def expectation(_object, _message, &_return_block)
|
34
|
+
raise NotImplementedError
|
35
35
|
end
|
36
36
|
|
37
37
|
def chain_on(object, *chain, &block)
|
@@ -42,7 +42,7 @@ module RSpec
|
|
42
42
|
def format_chain(*chain, &blk)
|
43
43
|
if Hash === chain.last
|
44
44
|
hash = chain.pop
|
45
|
-
hash.each do |k,v|
|
45
|
+
hash.each do |k, v|
|
46
46
|
chain << k
|
47
47
|
blk = Proc.new { v }
|
48
48
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Mocks
|
3
|
-
|
4
3
|
# A message expectation that only allows concrete return values to be set
|
5
4
|
# for a message. While this same effect can be achieved using a standard
|
6
5
|
# MessageExpecation, this version is much faster and so can be used as an
|
@@ -8,8 +7,7 @@ module RSpec
|
|
8
7
|
#
|
9
8
|
# @private
|
10
9
|
class SimpleMessageExpectation
|
11
|
-
|
12
|
-
def initialize(message, response, error_generator, backtrace_line = nil)
|
10
|
+
def initialize(message, response, error_generator, backtrace_line=nil)
|
13
11
|
@message, @response, @error_generator, @backtrace_line = message.to_sym, response, error_generator, backtrace_line
|
14
12
|
@received = false
|
15
13
|
end
|
@@ -45,6 +43,7 @@ module RSpec
|
|
45
43
|
attr_writer :expected_received_count, :expected_from, :argument_list_matcher
|
46
44
|
protected :expected_received_count=, :expected_from=, :error_generator, :error_generator=, :implementation=
|
47
45
|
|
46
|
+
# rubocop:disable Style/ParameterLists
|
48
47
|
# @private
|
49
48
|
def initialize(error_generator, expectation_ordering, expected_from, method_double,
|
50
49
|
type=:expectation, opts={}, &implementation_block)
|
@@ -70,6 +69,7 @@ module RSpec
|
|
70
69
|
@implementation = Implementation.new
|
71
70
|
self.inner_implementation_action = implementation_block
|
72
71
|
end
|
72
|
+
# rubocop:enable Style/ParameterLists
|
73
73
|
|
74
74
|
# @private
|
75
75
|
def expected_args
|
@@ -110,7 +110,7 @@ module RSpec
|
|
110
110
|
end
|
111
111
|
|
112
112
|
values.unshift(first_value)
|
113
|
-
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0
|
113
|
+
@expected_received_count = [@expected_received_count, values.size].max unless ignoring_args? || (@expected_received_count == 0 && @at_least)
|
114
114
|
self.terminal_implementation_action = AndReturnImplementation.new(values)
|
115
115
|
|
116
116
|
nil
|
@@ -137,11 +137,30 @@ module RSpec
|
|
137
137
|
# counter.increment
|
138
138
|
# expect(counter.count).to eq(original_count + 1)
|
139
139
|
def and_call_original
|
140
|
+
and_wrap_original do |original, *args, &block|
|
141
|
+
original.call(*args, &block)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# Decorates the stubbed method with the supplied block. The original
|
146
|
+
# unmodified method is passed to the block along with any method call
|
147
|
+
# arguments so you can delegate to it, whilst still being able to
|
148
|
+
# change what args are passed to it and/or change the return value.
|
149
|
+
#
|
150
|
+
# @note This is only available on partial doubles.
|
151
|
+
#
|
152
|
+
# @example
|
153
|
+
#
|
154
|
+
# expect(api).to receive(:large_list).and_wrap_original do |original_method, *args, &block|
|
155
|
+
# original_method.call(*args, &block).first(10)
|
156
|
+
# end
|
157
|
+
#
|
158
|
+
def and_wrap_original(&block)
|
140
159
|
if RSpec::Mocks::TestDouble === @method_double.object
|
141
160
|
@error_generator.raise_only_valid_on_a_partial_double(:and_call_original)
|
142
161
|
else
|
143
162
|
warn_about_stub_override if implementation.inner_action
|
144
|
-
@implementation =
|
163
|
+
@implementation = AndWrapOriginalImplementation.new(@method_double.original_method, block)
|
145
164
|
@yield_receiver_to_implementation_block = false
|
146
165
|
end
|
147
166
|
end
|
@@ -167,7 +186,7 @@ module RSpec
|
|
167
186
|
# allow(car).to receive(:go).and_raise(OutOfGas)
|
168
187
|
# allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive")
|
169
188
|
# allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz))
|
170
|
-
def and_raise(exception
|
189
|
+
def and_raise(exception=RuntimeError, message=nil)
|
171
190
|
if exception.respond_to?(:exception)
|
172
191
|
exception = message ? exception.exception(message) : exception.exception
|
173
192
|
end
|
@@ -234,7 +253,7 @@ module RSpec
|
|
234
253
|
|
235
254
|
# @private
|
236
255
|
def matches_name_but_not_args(message, *args)
|
237
|
-
@message == message
|
256
|
+
@message == message && !@argument_list_matcher.args_match?(*args)
|
238
257
|
end
|
239
258
|
|
240
259
|
# @private
|
@@ -296,7 +315,7 @@ module RSpec
|
|
296
315
|
def expectation_count_type
|
297
316
|
return :at_least if @at_least
|
298
317
|
return :at_most if @at_most
|
299
|
-
|
318
|
+
nil
|
300
319
|
end
|
301
320
|
|
302
321
|
# @private
|
@@ -335,7 +354,7 @@ module RSpec
|
|
335
354
|
def with(*args, &block)
|
336
355
|
if args.empty?
|
337
356
|
raise ArgumentError,
|
338
|
-
|
357
|
+
"`with` must have at least one argument. Use `no_args` matcher to set the expectation of receiving no arguments."
|
339
358
|
end
|
340
359
|
|
341
360
|
self.inner_implementation_action = block
|
@@ -430,6 +449,17 @@ module RSpec
|
|
430
449
|
self
|
431
450
|
end
|
432
451
|
|
452
|
+
# Expect a message to be received exactly three times.
|
453
|
+
#
|
454
|
+
# @example
|
455
|
+
#
|
456
|
+
# expect(car).to receive(:go).thrice
|
457
|
+
def thrice(&block)
|
458
|
+
self.inner_implementation_action = block
|
459
|
+
set_expected_received_count :exactly, 3
|
460
|
+
self
|
461
|
+
end
|
462
|
+
|
433
463
|
# Expect messages to be received in a specific order.
|
434
464
|
#
|
435
465
|
# @example
|
@@ -452,7 +482,6 @@ module RSpec
|
|
452
482
|
@expected_received_count - 1
|
453
483
|
end
|
454
484
|
|
455
|
-
|
456
485
|
# @private
|
457
486
|
def ordered?
|
458
487
|
@ordered
|
@@ -476,15 +505,13 @@ module RSpec
|
|
476
505
|
private
|
477
506
|
|
478
507
|
def invoke_incrementing_actual_calls_by(increment, parent_stub, *args, &block)
|
479
|
-
if yield_receiver_to_implementation_block?
|
480
|
-
args.unshift(orig_object)
|
481
|
-
end
|
508
|
+
args.unshift(orig_object) if yield_receiver_to_implementation_block?
|
482
509
|
|
483
510
|
if negative? || ((@exactly || @at_most) && (@actual_received_count == @expected_received_count))
|
484
511
|
@actual_received_count += increment
|
485
512
|
@failed_fast = true
|
486
|
-
#args are the args we actually received, @argument_list_matcher is the
|
487
|
-
#list of args we were expecting
|
513
|
+
# args are the args we actually received, @argument_list_matcher is the
|
514
|
+
# list of args we were expecting
|
488
515
|
@error_generator.raise_expectation_error(@message, @expected_received_count, @argument_list_matcher, @actual_received_count, expectation_count_type, *args)
|
489
516
|
end
|
490
517
|
|
@@ -513,6 +540,7 @@ module RSpec
|
|
513
540
|
when Numeric then n
|
514
541
|
when :once then 1
|
515
542
|
when :twice then 2
|
543
|
+
when :thrice then 3
|
516
544
|
end
|
517
545
|
end
|
518
546
|
|
@@ -532,7 +560,7 @@ module RSpec
|
|
532
560
|
|
533
561
|
def warn_about_stub_override
|
534
562
|
RSpec.warning(
|
535
|
-
"You're overriding a previous stub implementation of `#{@message}`. "
|
563
|
+
"You're overriding a previous stub implementation of `#{@message}`. " \
|
536
564
|
"Called from #{CallerFilter.first_non_rspec_line}."
|
537
565
|
)
|
538
566
|
end
|
@@ -547,7 +575,7 @@ module RSpec
|
|
547
575
|
@error_generator = error_generator
|
548
576
|
end
|
549
577
|
|
550
|
-
def call(*
|
578
|
+
def call(*_args_to_ignore, &block)
|
551
579
|
return if @args_to_yield.empty? && @eval_context.nil?
|
552
580
|
|
553
581
|
@error_generator.raise_missing_block_error @args_to_yield unless block
|
@@ -555,7 +583,7 @@ module RSpec
|
|
555
583
|
block_signature = Support::BlockSignature.new(block)
|
556
584
|
|
557
585
|
@args_to_yield.each do |args|
|
558
|
-
unless Support::
|
586
|
+
unless Support::StrictSignatureVerifier.new(block_signature, args).valid?
|
559
587
|
@error_generator.raise_wrong_arity_error(args, block_signature)
|
560
588
|
end
|
561
589
|
|
@@ -572,7 +600,7 @@ module RSpec
|
|
572
600
|
@values_to_return = values_to_return
|
573
601
|
end
|
574
602
|
|
575
|
-
def call(*
|
603
|
+
def call(*_args_to_ignore, &_block)
|
576
604
|
if @values_to_return.size > 1
|
577
605
|
@values_to_return.shift
|
578
606
|
else
|
@@ -606,22 +634,23 @@ module RSpec
|
|
606
634
|
|
607
635
|
# Represents an `and_call_original` implementation.
|
608
636
|
# @private
|
609
|
-
class
|
610
|
-
def initialize(method)
|
637
|
+
class AndWrapOriginalImplementation
|
638
|
+
def initialize(method, block)
|
611
639
|
@method = method
|
640
|
+
@block = block
|
612
641
|
end
|
613
642
|
|
614
643
|
CannotModifyFurtherError = Class.new(StandardError)
|
615
644
|
|
616
|
-
def initial_action=(
|
645
|
+
def initial_action=(_value)
|
617
646
|
raise cannot_modify_further_error
|
618
647
|
end
|
619
648
|
|
620
|
-
def inner_action=(
|
649
|
+
def inner_action=(_value)
|
621
650
|
raise cannot_modify_further_error
|
622
651
|
end
|
623
652
|
|
624
|
-
def terminal_action=(
|
653
|
+
def terminal_action=(_value)
|
625
654
|
raise cannot_modify_further_error
|
626
655
|
end
|
627
656
|
|
@@ -634,13 +663,13 @@ module RSpec
|
|
634
663
|
end
|
635
664
|
|
636
665
|
def call(*args, &block)
|
637
|
-
@
|
666
|
+
@block.call(@method, *args, &block)
|
638
667
|
end
|
639
668
|
|
640
669
|
private
|
641
670
|
|
642
671
|
def cannot_modify_further_error
|
643
|
-
CannotModifyFurtherError.new "This method has already been configured "
|
672
|
+
CannotModifyFurtherError.new "This method has already been configured " \
|
644
673
|
"to call the original implementation, and cannot be modified further."
|
645
674
|
end
|
646
675
|
end
|
@@ -653,9 +682,8 @@ module RSpec
|
|
653
682
|
yield
|
654
683
|
rescue RSpec::Mocks::MockExpectationError => error
|
655
684
|
error.backtrace.insert(0, location)
|
656
|
-
Kernel
|
685
|
+
Kernel.raise error
|
657
686
|
end
|
658
687
|
end
|
659
|
-
|
660
688
|
end
|
661
689
|
end
|