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