rspec-mocks 3.8.1
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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +3 -0
- data/.document +5 -0
- data/.yardopts +6 -0
- data/Changelog.md +1108 -0
- data/LICENSE.md +25 -0
- data/README.md +460 -0
- data/lib/rspec/mocks.rb +130 -0
- data/lib/rspec/mocks/any_instance.rb +11 -0
- data/lib/rspec/mocks/any_instance/chain.rb +110 -0
- data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
- data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +31 -0
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +50 -0
- data/lib/rspec/mocks/any_instance/message_chains.rb +83 -0
- data/lib/rspec/mocks/any_instance/proxy.rb +116 -0
- data/lib/rspec/mocks/any_instance/recorder.rb +289 -0
- data/lib/rspec/mocks/any_instance/stub_chain.rb +51 -0
- data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +23 -0
- data/lib/rspec/mocks/argument_list_matcher.rb +100 -0
- data/lib/rspec/mocks/argument_matchers.rb +320 -0
- data/lib/rspec/mocks/configuration.rb +212 -0
- data/lib/rspec/mocks/error_generator.rb +369 -0
- data/lib/rspec/mocks/example_methods.rb +434 -0
- data/lib/rspec/mocks/instance_method_stasher.rb +146 -0
- data/lib/rspec/mocks/marshal_extension.rb +41 -0
- data/lib/rspec/mocks/matchers/expectation_customization.rb +20 -0
- data/lib/rspec/mocks/matchers/have_received.rb +134 -0
- data/lib/rspec/mocks/matchers/receive.rb +132 -0
- data/lib/rspec/mocks/matchers/receive_message_chain.rb +82 -0
- data/lib/rspec/mocks/matchers/receive_messages.rb +77 -0
- data/lib/rspec/mocks/message_chain.rb +87 -0
- data/lib/rspec/mocks/message_expectation.rb +741 -0
- data/lib/rspec/mocks/method_double.rb +287 -0
- data/lib/rspec/mocks/method_reference.rb +202 -0
- data/lib/rspec/mocks/minitest_integration.rb +68 -0
- data/lib/rspec/mocks/mutate_const.rb +339 -0
- data/lib/rspec/mocks/object_reference.rb +149 -0
- data/lib/rspec/mocks/order_group.rb +81 -0
- data/lib/rspec/mocks/proxy.rb +485 -0
- data/lib/rspec/mocks/space.rb +238 -0
- data/lib/rspec/mocks/standalone.rb +3 -0
- data/lib/rspec/mocks/syntax.rb +325 -0
- data/lib/rspec/mocks/targets.rb +124 -0
- data/lib/rspec/mocks/test_double.rb +171 -0
- data/lib/rspec/mocks/verifying_double.rb +129 -0
- data/lib/rspec/mocks/verifying_message_expectation.rb +54 -0
- data/lib/rspec/mocks/verifying_proxy.rb +220 -0
- data/lib/rspec/mocks/version.rb +9 -0
- metadata +221 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Mocks
|
3
|
+
# @private
|
4
|
+
class OrderGroup
|
5
|
+
def initialize
|
6
|
+
@expectations = []
|
7
|
+
@invocation_order = []
|
8
|
+
@index = 0
|
9
|
+
end
|
10
|
+
|
11
|
+
# @private
|
12
|
+
def register(expectation)
|
13
|
+
@expectations << expectation
|
14
|
+
end
|
15
|
+
|
16
|
+
def invoked(message)
|
17
|
+
@invocation_order << message
|
18
|
+
end
|
19
|
+
|
20
|
+
# @private
|
21
|
+
def ready_for?(expectation)
|
22
|
+
remaining_expectations.find(&:ordered?) == expectation
|
23
|
+
end
|
24
|
+
|
25
|
+
# @private
|
26
|
+
def consume
|
27
|
+
remaining_expectations.each_with_index do |expectation, index|
|
28
|
+
next unless expectation.ordered?
|
29
|
+
|
30
|
+
@index += index + 1
|
31
|
+
return expectation
|
32
|
+
end
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# @private
|
37
|
+
def handle_order_constraint(expectation)
|
38
|
+
return unless expectation.ordered? && remaining_expectations.include?(expectation)
|
39
|
+
return consume if ready_for?(expectation)
|
40
|
+
expectation.raise_out_of_order_error
|
41
|
+
end
|
42
|
+
|
43
|
+
def verify_invocation_order(expectation)
|
44
|
+
expectation.raise_out_of_order_error unless expectations_invoked_in_order?
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def clear
|
49
|
+
@index = 0
|
50
|
+
@invocation_order.clear
|
51
|
+
@expectations.clear
|
52
|
+
end
|
53
|
+
|
54
|
+
def empty?
|
55
|
+
@expectations.empty?
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def remaining_expectations
|
61
|
+
@expectations[@index..-1] || []
|
62
|
+
end
|
63
|
+
|
64
|
+
def expectations_invoked_in_order?
|
65
|
+
invoked_expectations == expected_invocations
|
66
|
+
end
|
67
|
+
|
68
|
+
def invoked_expectations
|
69
|
+
@expectations.select { |e| e.ordered? && @invocation_order.include?(e) }
|
70
|
+
end
|
71
|
+
|
72
|
+
def expected_invocations
|
73
|
+
@invocation_order.map { |invocation| expectation_for(invocation) }.compact
|
74
|
+
end
|
75
|
+
|
76
|
+
def expectation_for(message)
|
77
|
+
@expectations.find { |e| message == e }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,485 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Mocks
|
3
|
+
# @private
|
4
|
+
class Proxy
|
5
|
+
# @private
|
6
|
+
SpecificMessage = Struct.new(:object, :message, :args) do
|
7
|
+
def ==(expectation)
|
8
|
+
expectation.orig_object == object && expectation.matches?(message, *args)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# @private
|
13
|
+
def ensure_implemented(*_args)
|
14
|
+
# noop for basic proxies, see VerifyingProxy for behaviour.
|
15
|
+
end
|
16
|
+
|
17
|
+
# @private
|
18
|
+
def initialize(object, order_group, options={})
|
19
|
+
@object = object
|
20
|
+
@order_group = order_group
|
21
|
+
@error_generator = ErrorGenerator.new(object)
|
22
|
+
@messages_received = []
|
23
|
+
@options = options
|
24
|
+
@null_object = false
|
25
|
+
@method_doubles = Hash.new { |h, k| h[k] = MethodDouble.new(@object, k, self) }
|
26
|
+
end
|
27
|
+
|
28
|
+
# @private
|
29
|
+
attr_reader :object
|
30
|
+
|
31
|
+
# @private
|
32
|
+
def null_object?
|
33
|
+
@null_object
|
34
|
+
end
|
35
|
+
|
36
|
+
# @private
|
37
|
+
# Tells the object to ignore any messages that aren't explicitly set as
|
38
|
+
# stubs or message expectations.
|
39
|
+
def as_null_object
|
40
|
+
@null_object = true
|
41
|
+
@object
|
42
|
+
end
|
43
|
+
|
44
|
+
# @private
|
45
|
+
def original_method_handle_for(_message)
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
DEFAULT_MESSAGE_EXPECTATION_OPTS = {}.freeze
|
50
|
+
|
51
|
+
# @private
|
52
|
+
def add_message_expectation(method_name, opts=DEFAULT_MESSAGE_EXPECTATION_OPTS, &block)
|
53
|
+
location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line }
|
54
|
+
meth_double = method_double_for(method_name)
|
55
|
+
|
56
|
+
if null_object? && !block
|
57
|
+
meth_double.add_default_stub(@error_generator, @order_group, location, opts) do
|
58
|
+
@object
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
meth_double.add_expectation @error_generator, @order_group, location, opts, &block
|
63
|
+
end
|
64
|
+
|
65
|
+
# @private
|
66
|
+
def add_simple_expectation(method_name, response, location)
|
67
|
+
method_double_for(method_name).add_simple_expectation method_name, response, @error_generator, location
|
68
|
+
end
|
69
|
+
|
70
|
+
# @private
|
71
|
+
def build_expectation(method_name)
|
72
|
+
meth_double = method_double_for(method_name)
|
73
|
+
|
74
|
+
meth_double.build_expectation(
|
75
|
+
@error_generator,
|
76
|
+
@order_group
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
# @private
|
81
|
+
def replay_received_message_on(expectation, &block)
|
82
|
+
expected_method_name = expectation.message
|
83
|
+
meth_double = method_double_for(expected_method_name)
|
84
|
+
|
85
|
+
if meth_double.expectations.any?
|
86
|
+
@error_generator.raise_expectation_on_mocked_method(expected_method_name)
|
87
|
+
end
|
88
|
+
|
89
|
+
unless null_object? || meth_double.stubs.any?
|
90
|
+
@error_generator.raise_expectation_on_unstubbed_method(expected_method_name)
|
91
|
+
end
|
92
|
+
|
93
|
+
@messages_received.each do |(actual_method_name, args, received_block)|
|
94
|
+
next unless expectation.matches?(actual_method_name, *args)
|
95
|
+
|
96
|
+
expectation.safe_invoke(nil)
|
97
|
+
block.call(*args, &received_block) if block
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# @private
|
102
|
+
def check_for_unexpected_arguments(expectation)
|
103
|
+
return if @messages_received.empty?
|
104
|
+
|
105
|
+
return if @messages_received.any? { |method_name, args, _| expectation.matches?(method_name, *args) }
|
106
|
+
|
107
|
+
name_but_not_args, others = @messages_received.partition do |(method_name, args, _)|
|
108
|
+
expectation.matches_name_but_not_args(method_name, *args)
|
109
|
+
end
|
110
|
+
|
111
|
+
return if name_but_not_args.empty? && !others.empty?
|
112
|
+
|
113
|
+
expectation.raise_unexpected_message_args_error(name_but_not_args.map { |args| args[1] })
|
114
|
+
end
|
115
|
+
|
116
|
+
# @private
|
117
|
+
def add_stub(method_name, opts={}, &implementation)
|
118
|
+
location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line }
|
119
|
+
method_double_for(method_name).add_stub @error_generator, @order_group, location, opts, &implementation
|
120
|
+
end
|
121
|
+
|
122
|
+
# @private
|
123
|
+
def add_simple_stub(method_name, response)
|
124
|
+
method_double_for(method_name).add_simple_stub method_name, response
|
125
|
+
end
|
126
|
+
|
127
|
+
# @private
|
128
|
+
def remove_stub(method_name)
|
129
|
+
method_double_for(method_name).remove_stub
|
130
|
+
end
|
131
|
+
|
132
|
+
# @private
|
133
|
+
def remove_stub_if_present(method_name)
|
134
|
+
method_double_for(method_name).remove_stub_if_present
|
135
|
+
end
|
136
|
+
|
137
|
+
# @private
|
138
|
+
def verify
|
139
|
+
@method_doubles.each_value { |d| d.verify }
|
140
|
+
end
|
141
|
+
|
142
|
+
# @private
|
143
|
+
def reset
|
144
|
+
@messages_received.clear
|
145
|
+
end
|
146
|
+
|
147
|
+
# @private
|
148
|
+
def received_message?(method_name, *args, &block)
|
149
|
+
@messages_received.any? { |array| array == [method_name, args, block] }
|
150
|
+
end
|
151
|
+
|
152
|
+
# @private
|
153
|
+
def messages_arg_list
|
154
|
+
@messages_received.map { |_, args, _| args }
|
155
|
+
end
|
156
|
+
|
157
|
+
# @private
|
158
|
+
def has_negative_expectation?(message)
|
159
|
+
method_double_for(message).expectations.find { |expectation| expectation.negative_expectation_for?(message) }
|
160
|
+
end
|
161
|
+
|
162
|
+
# @private
|
163
|
+
def record_message_received(message, *args, &block)
|
164
|
+
@order_group.invoked SpecificMessage.new(object, message, args)
|
165
|
+
@messages_received << [message, args, block]
|
166
|
+
end
|
167
|
+
|
168
|
+
# @private
|
169
|
+
def message_received(message, *args, &block)
|
170
|
+
record_message_received message, *args, &block
|
171
|
+
|
172
|
+
expectation = find_matching_expectation(message, *args)
|
173
|
+
stub = find_matching_method_stub(message, *args)
|
174
|
+
|
175
|
+
if (stub && expectation && expectation.called_max_times?) || (stub && !expectation)
|
176
|
+
expectation.increase_actual_received_count! if expectation && expectation.actual_received_count_matters?
|
177
|
+
if (expectation = find_almost_matching_expectation(message, *args))
|
178
|
+
expectation.advise(*args) unless expectation.expected_messages_received?
|
179
|
+
end
|
180
|
+
stub.invoke(nil, *args, &block)
|
181
|
+
elsif expectation
|
182
|
+
expectation.unadvise(messages_arg_list)
|
183
|
+
expectation.invoke(stub, *args, &block)
|
184
|
+
elsif (expectation = find_almost_matching_expectation(message, *args))
|
185
|
+
expectation.advise(*args) if null_object? unless expectation.expected_messages_received?
|
186
|
+
|
187
|
+
if null_object? || !has_negative_expectation?(message)
|
188
|
+
expectation.raise_unexpected_message_args_error([args])
|
189
|
+
end
|
190
|
+
elsif (stub = find_almost_matching_stub(message, *args))
|
191
|
+
stub.advise(*args)
|
192
|
+
raise_missing_default_stub_error(stub, [args])
|
193
|
+
elsif Class === @object
|
194
|
+
@object.superclass.__send__(message, *args, &block)
|
195
|
+
else
|
196
|
+
@object.__send__(:method_missing, message, *args, &block)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# @private
|
201
|
+
def raise_unexpected_message_error(method_name, args)
|
202
|
+
@error_generator.raise_unexpected_message_error method_name, args
|
203
|
+
end
|
204
|
+
|
205
|
+
# @private
|
206
|
+
def raise_missing_default_stub_error(expectation, args_for_multiple_calls)
|
207
|
+
@error_generator.raise_missing_default_stub_error(expectation, args_for_multiple_calls)
|
208
|
+
end
|
209
|
+
|
210
|
+
# @private
|
211
|
+
def visibility_for(_method_name)
|
212
|
+
# This is the default (for test doubles). Subclasses override this.
|
213
|
+
:public
|
214
|
+
end
|
215
|
+
|
216
|
+
if Support::RubyFeatures.module_prepends_supported?
|
217
|
+
def self.prepended_modules_of(klass)
|
218
|
+
ancestors = klass.ancestors
|
219
|
+
|
220
|
+
# `|| 0` is necessary for Ruby 2.0, where the singleton class
|
221
|
+
# is only in the ancestor list when there are prepended modules.
|
222
|
+
singleton_index = ancestors.index(klass) || 0
|
223
|
+
|
224
|
+
ancestors[0, singleton_index]
|
225
|
+
end
|
226
|
+
|
227
|
+
def prepended_modules_of_singleton_class
|
228
|
+
@prepended_modules_of_singleton_class ||= RSpec::Mocks::Proxy.prepended_modules_of(@object.singleton_class)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# @private
|
233
|
+
def method_double_if_exists_for_message(message)
|
234
|
+
method_double_for(message) if @method_doubles.key?(message.to_sym)
|
235
|
+
end
|
236
|
+
|
237
|
+
private
|
238
|
+
|
239
|
+
def method_double_for(message)
|
240
|
+
@method_doubles[message.to_sym]
|
241
|
+
end
|
242
|
+
|
243
|
+
def find_matching_expectation(method_name, *args)
|
244
|
+
find_best_matching_expectation_for(method_name) do |expectation|
|
245
|
+
expectation.matches?(method_name, *args)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
def find_almost_matching_expectation(method_name, *args)
|
250
|
+
find_best_matching_expectation_for(method_name) do |expectation|
|
251
|
+
expectation.matches_name_but_not_args(method_name, *args)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def find_best_matching_expectation_for(method_name)
|
256
|
+
first_match = nil
|
257
|
+
|
258
|
+
method_double_for(method_name).expectations.each do |expectation|
|
259
|
+
next unless yield expectation
|
260
|
+
return expectation unless expectation.called_max_times?
|
261
|
+
first_match ||= expectation
|
262
|
+
end
|
263
|
+
|
264
|
+
first_match
|
265
|
+
end
|
266
|
+
|
267
|
+
def find_matching_method_stub(method_name, *args)
|
268
|
+
method_double_for(method_name).stubs.find { |stub| stub.matches?(method_name, *args) }
|
269
|
+
end
|
270
|
+
|
271
|
+
def find_almost_matching_stub(method_name, *args)
|
272
|
+
method_double_for(method_name).stubs.find { |stub| stub.matches_name_but_not_args(method_name, *args) }
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
# @private
|
277
|
+
class TestDoubleProxy < Proxy
|
278
|
+
def reset
|
279
|
+
@method_doubles.clear
|
280
|
+
object.__disallow_further_usage!
|
281
|
+
super
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
# @private
|
286
|
+
class PartialDoubleProxy < Proxy
|
287
|
+
def original_method_handle_for(message)
|
288
|
+
if any_instance_class_recorder_observing_method?(@object.class, message)
|
289
|
+
message = ::RSpec::Mocks.space.
|
290
|
+
any_instance_recorder_for(@object.class).
|
291
|
+
build_alias_method_name(message)
|
292
|
+
end
|
293
|
+
|
294
|
+
::RSpec::Support.method_handle_for(@object, message)
|
295
|
+
rescue NameError
|
296
|
+
nil
|
297
|
+
end
|
298
|
+
|
299
|
+
# @private
|
300
|
+
def add_simple_expectation(method_name, response, location)
|
301
|
+
method_double_for(method_name).configure_method
|
302
|
+
super
|
303
|
+
end
|
304
|
+
|
305
|
+
# @private
|
306
|
+
def add_simple_stub(method_name, response)
|
307
|
+
method_double_for(method_name).configure_method
|
308
|
+
super
|
309
|
+
end
|
310
|
+
|
311
|
+
# @private
|
312
|
+
def visibility_for(method_name)
|
313
|
+
# We fall back to :public because by default we allow undefined methods
|
314
|
+
# to be stubbed, and when we do so, we make them public.
|
315
|
+
MethodReference.method_visibility_for(@object, method_name) || :public
|
316
|
+
end
|
317
|
+
|
318
|
+
def reset
|
319
|
+
@method_doubles.each_value { |d| d.reset }
|
320
|
+
super
|
321
|
+
end
|
322
|
+
|
323
|
+
def message_received(message, *args, &block)
|
324
|
+
RSpec::Mocks.space.any_instance_recorders_from_ancestry_of(object).each do |subscriber|
|
325
|
+
subscriber.notify_received_message(object, message, args, block)
|
326
|
+
end
|
327
|
+
super
|
328
|
+
end
|
329
|
+
|
330
|
+
private
|
331
|
+
|
332
|
+
def any_instance_class_recorder_observing_method?(klass, method_name)
|
333
|
+
only_return_existing = true
|
334
|
+
recorder = ::RSpec::Mocks.space.any_instance_recorder_for(klass, only_return_existing)
|
335
|
+
return true if recorder && recorder.already_observing?(method_name)
|
336
|
+
|
337
|
+
superklass = klass.superclass
|
338
|
+
return false if superklass.nil?
|
339
|
+
any_instance_class_recorder_observing_method?(superklass, method_name)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
# @private
|
344
|
+
# When we mock or stub a method on a class, we have to treat it a bit different,
|
345
|
+
# because normally singleton method definitions only affect the object on which
|
346
|
+
# they are defined, but on classes they affect subclasses, too. As a result,
|
347
|
+
# we need some special handling to get the original method.
|
348
|
+
module PartialClassDoubleProxyMethods
|
349
|
+
def initialize(source_space, *args)
|
350
|
+
@source_space = source_space
|
351
|
+
super(*args)
|
352
|
+
end
|
353
|
+
|
354
|
+
# Consider this situation:
|
355
|
+
#
|
356
|
+
# class A; end
|
357
|
+
# class B < A; end
|
358
|
+
#
|
359
|
+
# allow(A).to receive(:new)
|
360
|
+
# expect(B).to receive(:new).and_call_original
|
361
|
+
#
|
362
|
+
# When getting the original definition for `B.new`, we cannot rely purely on
|
363
|
+
# using `B.method(:new)` before our redefinition is defined on `B`, because
|
364
|
+
# `B.method(:new)` will return a method that will execute the stubbed version
|
365
|
+
# of the method on `A` since singleton methods on classes are in the lookup
|
366
|
+
# hierarchy.
|
367
|
+
#
|
368
|
+
# To do it properly, we need to find the original definition of `new` from `A`
|
369
|
+
# from _before_ `A` was stubbed, and we need to rebind it to `B` so that it will
|
370
|
+
# run with the proper `self`.
|
371
|
+
#
|
372
|
+
# That's what this method (together with `original_unbound_method_handle_from_ancestor_for`)
|
373
|
+
# does.
|
374
|
+
def original_method_handle_for(message)
|
375
|
+
unbound_method = superclass_proxy &&
|
376
|
+
superclass_proxy.original_unbound_method_handle_from_ancestor_for(message.to_sym)
|
377
|
+
|
378
|
+
return super unless unbound_method
|
379
|
+
unbound_method.bind(object)
|
380
|
+
# :nocov:
|
381
|
+
rescue TypeError
|
382
|
+
if RUBY_VERSION == '1.8.7'
|
383
|
+
# In MRI 1.8.7, a singleton method on a class cannot be rebound to its subclass
|
384
|
+
if unbound_method && unbound_method.owner.ancestors.first != unbound_method.owner
|
385
|
+
# This is a singleton method; we can't do anything with it
|
386
|
+
# But we can work around this using a different implementation
|
387
|
+
double = method_double_from_ancestor_for(message)
|
388
|
+
return object.method(double.method_stasher.stashed_method_name)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
raise
|
392
|
+
# :nocov:
|
393
|
+
end
|
394
|
+
|
395
|
+
protected
|
396
|
+
|
397
|
+
def original_unbound_method_handle_from_ancestor_for(message)
|
398
|
+
double = method_double_from_ancestor_for(message)
|
399
|
+
double && double.original_method.unbind
|
400
|
+
end
|
401
|
+
|
402
|
+
def method_double_from_ancestor_for(message)
|
403
|
+
@method_doubles.fetch(message) do
|
404
|
+
# The fact that there is no method double for this message indicates
|
405
|
+
# that it has not been redefined by rspec-mocks. We need to continue
|
406
|
+
# looking up the ancestor chain.
|
407
|
+
return superclass_proxy &&
|
408
|
+
superclass_proxy.method_double_from_ancestor_for(message)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
def superclass_proxy
|
413
|
+
return @superclass_proxy if defined?(@superclass_proxy)
|
414
|
+
|
415
|
+
if (superclass = object.superclass)
|
416
|
+
@superclass_proxy = @source_space.superclass_proxy_for(superclass)
|
417
|
+
else
|
418
|
+
@superclass_proxy = nil
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
# @private
|
424
|
+
class PartialClassDoubleProxy < PartialDoubleProxy
|
425
|
+
include PartialClassDoubleProxyMethods
|
426
|
+
end
|
427
|
+
|
428
|
+
# @private
|
429
|
+
class ProxyForNil < PartialDoubleProxy
|
430
|
+
def initialize(order_group)
|
431
|
+
set_expectation_behavior
|
432
|
+
super(nil, order_group)
|
433
|
+
end
|
434
|
+
|
435
|
+
attr_accessor :disallow_expectations
|
436
|
+
attr_accessor :warn_about_expectations
|
437
|
+
|
438
|
+
def add_message_expectation(method_name, opts={}, &block)
|
439
|
+
warn_or_raise!(method_name)
|
440
|
+
super
|
441
|
+
end
|
442
|
+
|
443
|
+
def add_stub(method_name, opts={}, &implementation)
|
444
|
+
warn_or_raise!(method_name)
|
445
|
+
super
|
446
|
+
end
|
447
|
+
|
448
|
+
private
|
449
|
+
|
450
|
+
def set_expectation_behavior
|
451
|
+
case RSpec::Mocks.configuration.allow_message_expectations_on_nil
|
452
|
+
when false
|
453
|
+
@warn_about_expectations = false
|
454
|
+
@disallow_expectations = true
|
455
|
+
when true
|
456
|
+
@warn_about_expectations = false
|
457
|
+
@disallow_expectations = false
|
458
|
+
else
|
459
|
+
@warn_about_expectations = true
|
460
|
+
@disallow_expectations = false
|
461
|
+
end
|
462
|
+
end
|
463
|
+
|
464
|
+
def warn_or_raise!(method_name)
|
465
|
+
# This method intentionally swallows the message when
|
466
|
+
# neither disallow_expectations nor warn_about_expectations
|
467
|
+
# are set to true.
|
468
|
+
if disallow_expectations
|
469
|
+
raise_error(method_name)
|
470
|
+
elsif warn_about_expectations
|
471
|
+
warn(method_name)
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
def warn(method_name)
|
476
|
+
warning_msg = @error_generator.expectation_on_nil_message(method_name)
|
477
|
+
RSpec.warning(warning_msg)
|
478
|
+
end
|
479
|
+
|
480
|
+
def raise_error(method_name)
|
481
|
+
@error_generator.raise_expectation_on_nil_error(method_name)
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|