rspec-mocks 3.2.1 → 3.3.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 +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
|