rspec-mocks 3.2.1 → 3.3.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 +47 -0
- data/README.md +2 -2
- data/lib/rspec/mocks/any_instance.rb +1 -0
- data/lib/rspec/mocks/any_instance/chain.rb +1 -1
- data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
- data/lib/rspec/mocks/any_instance/message_chains.rb +1 -3
- data/lib/rspec/mocks/any_instance/recorder.rb +7 -10
- data/lib/rspec/mocks/argument_list_matcher.rb +3 -3
- data/lib/rspec/mocks/configuration.rb +6 -5
- data/lib/rspec/mocks/error_generator.rb +163 -111
- data/lib/rspec/mocks/example_methods.rb +2 -5
- data/lib/rspec/mocks/matchers/have_received.rb +6 -9
- data/lib/rspec/mocks/message_chain.rb +0 -4
- data/lib/rspec/mocks/message_expectation.rb +56 -53
- data/lib/rspec/mocks/method_double.rb +13 -9
- data/lib/rspec/mocks/method_reference.rb +37 -0
- data/lib/rspec/mocks/proxy.rb +26 -18
- data/lib/rspec/mocks/test_double.rb +40 -5
- data/lib/rspec/mocks/verifying_double.rb +11 -16
- data/lib/rspec/mocks/verifying_proxy.rb +31 -7
- data/lib/rspec/mocks/version.rb +1 -1
- metadata +8 -7
- metadata.gz.sig +0 -0
@@ -396,13 +396,10 @@ module RSpec
|
|
396
396
|
if RSpec::Mocks.configuration.verify_doubled_constant_names? &&
|
397
397
|
!ref.defined?
|
398
398
|
|
399
|
-
|
400
|
-
"#{ref.description.inspect} is not a defined constant. " \
|
401
|
-
"Perhaps you misspelt it? " \
|
402
|
-
"Disable check with `verify_doubled_constant_names` configuration option."
|
399
|
+
RSpec::Mocks.error_generator.raise_verifying_double_not_defined_error(ref)
|
403
400
|
end
|
404
401
|
|
405
|
-
RSpec::Mocks.configuration.
|
402
|
+
RSpec::Mocks.configuration.verifying_double_callbacks.each do |block|
|
406
403
|
block.call(ref)
|
407
404
|
end
|
408
405
|
|
@@ -69,9 +69,7 @@ module RSpec
|
|
69
69
|
private
|
70
70
|
|
71
71
|
def disallow(type, reason="")
|
72
|
-
|
73
|
-
"Using #{type}(...) with the `have_received` " \
|
74
|
-
"matcher is not supported#{reason}."
|
72
|
+
RSpec::Mocks.error_generator.raise_have_received_disallowed(type, reason)
|
75
73
|
end
|
76
74
|
|
77
75
|
def expect
|
@@ -88,8 +86,7 @@ module RSpec
|
|
88
86
|
|
89
87
|
def ensure_count_unconstrained
|
90
88
|
return unless count_constraint
|
91
|
-
|
92
|
-
"can't use #{count_constraint} when negative"
|
89
|
+
RSpec::Mocks.error_generator.raise_cant_constrain_count_for_negated_have_received_error(count_constraint)
|
93
90
|
end
|
94
91
|
|
95
92
|
def count_constraint
|
@@ -99,10 +96,10 @@ module RSpec
|
|
99
96
|
end
|
100
97
|
|
101
98
|
def generate_failure_message
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
99
|
+
RSpec::Support.with_failure_notifier(Proc.new { |err, _opt| return err.message }) do
|
100
|
+
mock_proxy.check_for_unexpected_arguments(@expectation)
|
101
|
+
@expectation.generate_error
|
102
|
+
end
|
106
103
|
end
|
107
104
|
|
108
105
|
def expected_messages_received_in_order?
|
@@ -26,11 +26,13 @@ module RSpec
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def verify_messages_received
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
29
|
+
return if @received
|
30
|
+
@error_generator.raise_expectation_error(
|
31
|
+
@message, 1, ArgumentListMatcher::MATCH_ALL, 0, nil, [], @backtrace_line
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
def unadvise(_)
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
@@ -115,7 +117,7 @@ module RSpec
|
|
115
117
|
@error_generator.raise_only_valid_on_a_partial_double(:and_call_original)
|
116
118
|
else
|
117
119
|
warn_about_stub_override if implementation.inner_action
|
118
|
-
@implementation = AndWrapOriginalImplementation.new(@method_double.
|
120
|
+
@implementation = AndWrapOriginalImplementation.new(@method_double.original_implementation_callable, block)
|
119
121
|
@yield_receiver_to_implementation_block = false
|
120
122
|
end
|
121
123
|
|
@@ -142,13 +144,9 @@ module RSpec
|
|
142
144
|
# allow(car).to receive(:go).and_raise(OutOfGas)
|
143
145
|
# allow(car).to receive(:go).and_raise(OutOfGas, "At least 2 oz of gas needed to drive")
|
144
146
|
# allow(car).to receive(:go).and_raise(OutOfGas.new(2, :oz))
|
145
|
-
def and_raise(
|
147
|
+
def and_raise(*args)
|
146
148
|
raise_already_invoked_error_if_necessary(__method__)
|
147
|
-
|
148
|
-
exception = message ? exception.exception(message) : exception.exception
|
149
|
-
end
|
150
|
-
|
151
|
-
self.terminal_implementation_action = Proc.new { raise exception }
|
149
|
+
self.terminal_implementation_action = Proc.new { raise(*args) }
|
152
150
|
nil
|
153
151
|
end
|
154
152
|
|
@@ -177,6 +175,11 @@ module RSpec
|
|
177
175
|
def and_yield(*args, &block)
|
178
176
|
raise_already_invoked_error_if_necessary(__method__)
|
179
177
|
yield @eval_context = Object.new if block
|
178
|
+
|
179
|
+
# Initialize args to yield now that it's being used, see also: comment
|
180
|
+
# in constructor.
|
181
|
+
@args_to_yield ||= []
|
182
|
+
|
180
183
|
@args_to_yield << args
|
181
184
|
self.initial_implementation_action = AndYieldImplementation.new(@args_to_yield, @eval_context, @error_generator)
|
182
185
|
self
|
@@ -248,7 +251,7 @@ module RSpec
|
|
248
251
|
# @example
|
249
252
|
# expect(car).to receive(:stop).never
|
250
253
|
def never
|
251
|
-
|
254
|
+
error_generator.raise_double_negation_error("expect(obj)") if negative?
|
252
255
|
@expected_received_count = 0
|
253
256
|
self
|
254
257
|
end
|
@@ -371,8 +374,10 @@ module RSpec
|
|
371
374
|
@expectation_type = type
|
372
375
|
@ordered = false
|
373
376
|
@at_least = @at_most = @exactly = nil
|
374
|
-
|
375
|
-
|
377
|
+
|
378
|
+
# Initialized to nil so that we don't allocate an array for every
|
379
|
+
# mock or stub. See also comment in `and_yield`.
|
380
|
+
@args_to_yield = nil
|
376
381
|
@eval_context = nil
|
377
382
|
@yield_receiver_to_implementation_block = false
|
378
383
|
|
@@ -426,9 +431,8 @@ module RSpec
|
|
426
431
|
end
|
427
432
|
|
428
433
|
def verify_messages_received
|
429
|
-
|
430
|
-
|
431
|
-
end
|
434
|
+
return if expected_messages_received?
|
435
|
+
generate_error
|
432
436
|
end
|
433
437
|
|
434
438
|
def expected_messages_received?
|
@@ -464,14 +468,28 @@ module RSpec
|
|
464
468
|
similar_messages << args
|
465
469
|
end
|
466
470
|
|
471
|
+
def unadvise(args)
|
472
|
+
similar_messages.delete_if { |message| args.include?(message) }
|
473
|
+
end
|
474
|
+
|
467
475
|
def generate_error
|
468
476
|
if similar_messages.empty?
|
469
|
-
@error_generator.raise_expectation_error(
|
477
|
+
@error_generator.raise_expectation_error(
|
478
|
+
@message, @expected_received_count, @argument_list_matcher,
|
479
|
+
@actual_received_count, expectation_count_type, expected_args,
|
480
|
+
@expected_from, exception_source_id
|
481
|
+
)
|
470
482
|
else
|
471
|
-
@error_generator.raise_similar_message_args_error(
|
483
|
+
@error_generator.raise_similar_message_args_error(
|
484
|
+
self, @similar_messages, @expected_from
|
485
|
+
)
|
472
486
|
end
|
473
487
|
end
|
474
488
|
|
489
|
+
def raise_unexpected_message_args_error(args_for_multiple_calls)
|
490
|
+
@error_generator.raise_unexpected_message_args_error(self, args_for_multiple_calls, exception_source_id)
|
491
|
+
end
|
492
|
+
|
475
493
|
def expectation_count_type
|
476
494
|
return :at_least if @at_least
|
477
495
|
return :at_most if @at_most
|
@@ -481,7 +499,7 @@ module RSpec
|
|
481
499
|
def description_for(verb)
|
482
500
|
@error_generator.describe_expectation(
|
483
501
|
verb, @message, @expected_received_count,
|
484
|
-
@actual_received_count,
|
502
|
+
@actual_received_count, expected_args
|
485
503
|
)
|
486
504
|
end
|
487
505
|
|
@@ -512,28 +530,33 @@ module RSpec
|
|
512
530
|
|
513
531
|
private
|
514
532
|
|
533
|
+
def exception_source_id
|
534
|
+
@exception_source_id ||= "#{self.class.name} #{__id__}"
|
535
|
+
end
|
536
|
+
|
515
537
|
def invoke_incrementing_actual_calls_by(increment, allowed_to_fail, parent_stub, *args, &block)
|
516
538
|
args.unshift(orig_object) if yield_receiver_to_implementation_block?
|
517
539
|
|
518
540
|
if negative? || (allowed_to_fail && (@exactly || @at_most) && (@actual_received_count == @expected_received_count))
|
519
|
-
@actual_received_count += increment
|
520
|
-
@failed_fast = true
|
521
541
|
# args are the args we actually received, @argument_list_matcher is the
|
522
542
|
# list of args we were expecting
|
523
|
-
@error_generator.raise_expectation_error(
|
543
|
+
@error_generator.raise_expectation_error(
|
544
|
+
@message, @expected_received_count,
|
545
|
+
@argument_list_matcher,
|
546
|
+
@actual_received_count + increment,
|
547
|
+
expectation_count_type, args, nil, exception_source_id
|
548
|
+
)
|
524
549
|
end
|
525
550
|
|
526
551
|
@order_group.handle_order_constraint self
|
527
552
|
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
parent_stub.invoke(nil, *args, &block)
|
533
|
-
end
|
534
|
-
ensure
|
535
|
-
@actual_received_count += increment
|
553
|
+
if implementation.present?
|
554
|
+
implementation.call(*args, &block)
|
555
|
+
elsif parent_stub
|
556
|
+
parent_stub.invoke(nil, *args, &block)
|
536
557
|
end
|
558
|
+
ensure
|
559
|
+
@actual_received_count += increment
|
537
560
|
end
|
538
561
|
|
539
562
|
def has_been_invoked?
|
@@ -543,15 +566,7 @@ module RSpec
|
|
543
566
|
def raise_already_invoked_error_if_necessary(calling_customization)
|
544
567
|
return unless has_been_invoked?
|
545
568
|
|
546
|
-
|
547
|
-
"and cannot be modified further (e.g. using `#{calling_customization}`). All message expectation " \
|
548
|
-
"customizations must be applied before it is used for the first time."
|
549
|
-
|
550
|
-
raise MockExpectationAlreadyInvokedError, error_message
|
551
|
-
end
|
552
|
-
|
553
|
-
def failed_fast?
|
554
|
-
@failed_fast
|
569
|
+
error_generator.raise_already_invoked_error(message, calling_customization)
|
555
570
|
end
|
556
571
|
|
557
572
|
def set_expected_received_count(relativity, n)
|
@@ -698,17 +713,5 @@ module RSpec
|
|
698
713
|
"to call the original implementation, and cannot be modified further."
|
699
714
|
end
|
700
715
|
end
|
701
|
-
|
702
|
-
# Insert original locations into stacktraces
|
703
|
-
#
|
704
|
-
# @private
|
705
|
-
class InsertOntoBacktrace
|
706
|
-
def self.line(location)
|
707
|
-
yield
|
708
|
-
rescue RSpec::Mocks::MockExpectationError => error
|
709
|
-
error.backtrace.insert(0, location)
|
710
|
-
Kernel.raise error
|
711
|
-
end
|
712
|
-
end
|
713
716
|
end
|
714
717
|
end
|
@@ -18,20 +18,24 @@ module RSpec
|
|
18
18
|
@stubs = []
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
21
|
+
def original_implementation_callable
|
22
22
|
# If original method is not present, uses the `method_missing`
|
23
23
|
# handler of the object. This accounts for cases where the user has not
|
24
24
|
# correctly defined `respond_to?`, and also 1.8 which does not provide
|
25
25
|
# method handles for missing methods even if `respond_to?` is correct.
|
26
|
-
@original_method
|
27
|
-
@method_stasher.original_method ||
|
28
|
-
@proxy.original_method_handle_for(method_name) ||
|
26
|
+
@original_implementation_callable ||= original_method ||
|
29
27
|
Proc.new do |*args, &block|
|
30
28
|
@object.__send__(:method_missing, @method_name, *args, &block)
|
31
29
|
end
|
32
30
|
end
|
33
31
|
|
34
|
-
alias_method :
|
32
|
+
alias_method :save_original_implementation_callable!, :original_implementation_callable
|
33
|
+
|
34
|
+
def original_method
|
35
|
+
@original_method ||=
|
36
|
+
@method_stasher.original_method ||
|
37
|
+
@proxy.original_method_handle_for(method_name)
|
38
|
+
end
|
35
39
|
|
36
40
|
# @private
|
37
41
|
def visibility
|
@@ -45,7 +49,7 @@ module RSpec
|
|
45
49
|
|
46
50
|
# @private
|
47
51
|
def configure_method
|
48
|
-
@original_visibility =
|
52
|
+
@original_visibility = visibility
|
49
53
|
@method_stasher.stash unless @method_is_proxied
|
50
54
|
define_proxy_method
|
51
55
|
end
|
@@ -54,7 +58,7 @@ module RSpec
|
|
54
58
|
def define_proxy_method
|
55
59
|
return if @method_is_proxied
|
56
60
|
|
57
|
-
|
61
|
+
save_original_implementation_callable!
|
58
62
|
definition_target.class_exec(self, method_name, visibility) do |method_double, method_name, visibility|
|
59
63
|
define_method(method_name) do |*args, &block|
|
60
64
|
method_double.proxy_method_invoked(self, *args, &block)
|
@@ -101,7 +105,7 @@ module RSpec
|
|
101
105
|
return unless @original_visibility &&
|
102
106
|
MethodReference.method_defined_at_any_visibility?(object_singleton_class, @method_name)
|
103
107
|
|
104
|
-
object_singleton_class.__send__(
|
108
|
+
object_singleton_class.__send__(@original_visibility, method_name)
|
105
109
|
end
|
106
110
|
|
107
111
|
# @private
|
@@ -198,7 +202,7 @@ module RSpec
|
|
198
202
|
|
199
203
|
# @private
|
200
204
|
def raise_method_not_stubbed_error
|
201
|
-
|
205
|
+
RSpec::Mocks.error_generator.raise_method_not_stubbed_error(method_name)
|
202
206
|
end
|
203
207
|
|
204
208
|
# In Ruby 2.0.0 and above prepend will alter the method lookup chain.
|
@@ -6,6 +6,10 @@ module RSpec
|
|
6
6
|
#
|
7
7
|
# @private
|
8
8
|
class MethodReference
|
9
|
+
def self.for(object_reference, method_name)
|
10
|
+
new(object_reference, method_name)
|
11
|
+
end
|
12
|
+
|
9
13
|
def initialize(object_reference, method_name)
|
10
14
|
@object_reference = object_reference
|
11
15
|
@method_name = method_name
|
@@ -133,6 +137,14 @@ module RSpec
|
|
133
137
|
|
134
138
|
# @private
|
135
139
|
class ObjectMethodReference < MethodReference
|
140
|
+
def self.for(object_reference, method_name)
|
141
|
+
if ClassNewMethodReference.applies_to?(method_name) { object_reference.when_loaded { |o| o } }
|
142
|
+
ClassNewMethodReference.new(object_reference, method_name)
|
143
|
+
else
|
144
|
+
super
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
136
148
|
private
|
137
149
|
|
138
150
|
def method_implemented?(object)
|
@@ -151,5 +163,30 @@ module RSpec
|
|
151
163
|
MethodReference.method_visibility_for(object, @method_name)
|
152
164
|
end
|
153
165
|
end
|
166
|
+
|
167
|
+
# When a class's `.new` method is stubbed, we want to use the method
|
168
|
+
# signature from `#initialize` because `.new`'s signature is a generic
|
169
|
+
# `def new(*args)` and it simply delegates to `#initialize` and forwards
|
170
|
+
# all args...so the method with the actually used signature is `#initialize`.
|
171
|
+
#
|
172
|
+
# This method reference implementation handles that specific case.
|
173
|
+
# @private
|
174
|
+
class ClassNewMethodReference < ObjectMethodReference
|
175
|
+
def self.applies_to?(method_name)
|
176
|
+
return false unless method_name == :new
|
177
|
+
klass = yield
|
178
|
+
return false unless klass.respond_to?(:new, true)
|
179
|
+
|
180
|
+
# We only want to apply our special logic to normal `new` methods.
|
181
|
+
# Methods that the user has monkeyed with should be left as-is.
|
182
|
+
klass.method(:new).owner == ::Class
|
183
|
+
end
|
184
|
+
|
185
|
+
def with_signature
|
186
|
+
@object_reference.when_loaded do |klass|
|
187
|
+
yield Support::MethodSignature.new(klass.instance_method(:initialize))
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
154
191
|
end
|
155
192
|
end
|
data/lib/rspec/mocks/proxy.rb
CHANGED
@@ -14,11 +14,10 @@ module RSpec
|
|
14
14
|
end
|
15
15
|
|
16
16
|
# @private
|
17
|
-
def initialize(object, order_group,
|
17
|
+
def initialize(object, order_group, options={})
|
18
18
|
@object = object
|
19
19
|
@order_group = order_group
|
20
|
-
@
|
21
|
-
@error_generator = ErrorGenerator.new(object, name)
|
20
|
+
@error_generator = ErrorGenerator.new(object)
|
22
21
|
@messages_received = []
|
23
22
|
@options = options
|
24
23
|
@null_object = false
|
@@ -46,8 +45,10 @@ module RSpec
|
|
46
45
|
nil
|
47
46
|
end
|
48
47
|
|
48
|
+
DEFAULT_MESSAGE_EXPECTATION_OPTS = {}.freeze
|
49
|
+
|
49
50
|
# @private
|
50
|
-
def add_message_expectation(method_name, opts=
|
51
|
+
def add_message_expectation(method_name, opts=DEFAULT_MESSAGE_EXPECTATION_OPTS, &block)
|
51
52
|
location = opts.fetch(:expected_from) { CallerFilter.first_non_rspec_line }
|
52
53
|
meth_double = method_double_for(method_name)
|
53
54
|
|
@@ -98,11 +99,17 @@ module RSpec
|
|
98
99
|
|
99
100
|
# @private
|
100
101
|
def check_for_unexpected_arguments(expectation)
|
101
|
-
@messages_received.
|
102
|
-
|
102
|
+
return if @messages_received.empty?
|
103
|
+
|
104
|
+
return if @messages_received.any? { |method_name, args, _| expectation.matches?(method_name, *args) }
|
103
105
|
|
104
|
-
|
106
|
+
name_but_not_args, others = @messages_received.partition do |(method_name, args, _)|
|
107
|
+
expectation.matches_name_but_not_args(method_name, *args)
|
105
108
|
end
|
109
|
+
|
110
|
+
return if name_but_not_args.empty? && !others.empty?
|
111
|
+
|
112
|
+
expectation.raise_unexpected_message_args_error(name_but_not_args.map { |args| args[1] })
|
106
113
|
end
|
107
114
|
|
108
115
|
# @private
|
@@ -141,6 +148,11 @@ module RSpec
|
|
141
148
|
@messages_received.any? { |array| array == [method_name, args, block] }
|
142
149
|
end
|
143
150
|
|
151
|
+
# @private
|
152
|
+
def messages_arg_list
|
153
|
+
@messages_received.map { |_, args, _| args }
|
154
|
+
end
|
155
|
+
|
144
156
|
# @private
|
145
157
|
def has_negative_expectation?(message)
|
146
158
|
method_double_for(message).expectations.find { |expectation| expectation.negative_expectation_for?(message) }
|
@@ -166,16 +178,17 @@ module RSpec
|
|
166
178
|
end
|
167
179
|
stub.invoke(nil, *args, &block)
|
168
180
|
elsif expectation
|
181
|
+
expectation.unadvise(messages_arg_list)
|
169
182
|
expectation.invoke(stub, *args, &block)
|
170
183
|
elsif (expectation = find_almost_matching_expectation(message, *args))
|
171
184
|
expectation.advise(*args) if null_object? unless expectation.expected_messages_received?
|
172
185
|
|
173
186
|
if null_object? || !has_negative_expectation?(message)
|
174
|
-
raise_unexpected_message_args_error(
|
187
|
+
expectation.raise_unexpected_message_args_error([args])
|
175
188
|
end
|
176
189
|
elsif (stub = find_almost_matching_stub(message, *args))
|
177
190
|
stub.advise(*args)
|
178
|
-
raise_missing_default_stub_error(stub,
|
191
|
+
raise_missing_default_stub_error(stub, [args])
|
179
192
|
elsif Class === @object
|
180
193
|
@object.superclass.__send__(message, *args, &block)
|
181
194
|
else
|
@@ -184,18 +197,13 @@ module RSpec
|
|
184
197
|
end
|
185
198
|
|
186
199
|
# @private
|
187
|
-
def raise_unexpected_message_error(method_name,
|
188
|
-
@error_generator.raise_unexpected_message_error method_name,
|
189
|
-
end
|
190
|
-
|
191
|
-
# @private
|
192
|
-
def raise_unexpected_message_args_error(expectation, *args)
|
193
|
-
@error_generator.raise_unexpected_message_args_error(expectation, *args)
|
200
|
+
def raise_unexpected_message_error(method_name, args)
|
201
|
+
@error_generator.raise_unexpected_message_error method_name, args
|
194
202
|
end
|
195
203
|
|
196
204
|
# @private
|
197
|
-
def raise_missing_default_stub_error(expectation,
|
198
|
-
@error_generator.raise_missing_default_stub_error(expectation,
|
205
|
+
def raise_missing_default_stub_error(expectation, args_for_multiple_calls)
|
206
|
+
@error_generator.raise_missing_default_stub_error(expectation, args_for_multiple_calls)
|
199
207
|
end
|
200
208
|
|
201
209
|
# @private
|