rspec-mocks 3.1.3 → 3.2.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 +5 -3
- data/Changelog.md +65 -2
- data/README.md +25 -5
- data/lib/rspec/mocks/any_instance/chain.rb +1 -0
- data/lib/rspec/mocks/any_instance/stub_chain.rb +2 -1
- data/lib/rspec/mocks/argument_list_matcher.rb +36 -8
- data/lib/rspec/mocks/argument_matchers.rb +63 -16
- data/lib/rspec/mocks/configuration.rb +35 -0
- data/lib/rspec/mocks/error_generator.rb +69 -17
- data/lib/rspec/mocks/example_methods.rb +59 -6
- data/lib/rspec/mocks/matchers/have_received.rb +4 -6
- data/lib/rspec/mocks/matchers/receive.rb +25 -1
- data/lib/rspec/mocks/matchers/receive_message_chain.rb +14 -0
- data/lib/rspec/mocks/matchers/receive_messages.rb +4 -0
- data/lib/rspec/mocks/message_expectation.rb +308 -277
- data/lib/rspec/mocks/method_double.rb +25 -2
- data/lib/rspec/mocks/mutate_const.rb +19 -8
- data/lib/rspec/mocks/object_reference.rb +81 -23
- data/lib/rspec/mocks/proxy.rb +42 -4
- data/lib/rspec/mocks/standalone.rb +1 -1
- data/lib/rspec/mocks/verifying_double.rb +17 -10
- data/lib/rspec/mocks/verifying_message_expecation.rb +9 -17
- data/lib/rspec/mocks/verifying_proxy.rb +4 -2
- data/lib/rspec/mocks/version.rb +1 -1
- metadata +57 -25
- metadata.gz.sig +0 -0
@@ -78,8 +78,7 @@ module RSpec
|
|
78
78
|
return show_frozen_warning if object_singleton_class.frozen?
|
79
79
|
return unless @method_is_proxied
|
80
80
|
|
81
|
-
|
82
|
-
|
81
|
+
remove_method_from_definition_target
|
83
82
|
@method_stasher.restore if @method_stasher.method_is_stashed?
|
84
83
|
restore_original_visibility
|
85
84
|
|
@@ -255,6 +254,30 @@ module RSpec
|
|
255
254
|
end
|
256
255
|
|
257
256
|
end
|
257
|
+
|
258
|
+
private
|
259
|
+
|
260
|
+
def remove_method_from_definition_target
|
261
|
+
definition_target.__send__(:remove_method, @method_name)
|
262
|
+
rescue NameError
|
263
|
+
# This can happen when the method has been monkeyed with by
|
264
|
+
# something outside RSpec. This happens, for example, when
|
265
|
+
# `file.write` has been stubbed, and then `file.reopen(other_io)`
|
266
|
+
# is later called, as `File#reopen` appears to redefine `write`.
|
267
|
+
#
|
268
|
+
# Note: we could avoid rescuing this by checking
|
269
|
+
# `definition_target.instance_method(@method_name).owner == definition_target`,
|
270
|
+
# saving us from the cost of the expensive exception, but this error is
|
271
|
+
# extremely rare (it was discovered on 2014-12-30, only happens on
|
272
|
+
# RUBY_VERSION < 2.0 and our spec suite only hits this condition once),
|
273
|
+
# so we'd rather avoid the cost of that check for every method double,
|
274
|
+
# and risk the rare situation where this exception will get raised.
|
275
|
+
RSpec.warn_with(
|
276
|
+
"WARNING: RSpec could not fully restore #{@object.inspect}." \
|
277
|
+
"#{@method_name}, possibly because the method has been redefined " \
|
278
|
+
"by something outside of RSpec."
|
279
|
+
)
|
280
|
+
end
|
258
281
|
end
|
259
282
|
end
|
260
283
|
end
|
@@ -13,6 +13,8 @@ module RSpec
|
|
13
13
|
@previously_defined = false
|
14
14
|
@stubbed = false
|
15
15
|
@hidden = false
|
16
|
+
@valid_name = true
|
17
|
+
yield self if block_given?
|
16
18
|
end
|
17
19
|
|
18
20
|
# @return [String] The fully qualified name of the constant.
|
@@ -24,7 +26,7 @@ module RSpec
|
|
24
26
|
attr_accessor :original_value
|
25
27
|
|
26
28
|
# @private
|
27
|
-
attr_writer :previously_defined, :stubbed, :hidden
|
29
|
+
attr_writer :previously_defined, :stubbed, :hidden, :valid_name
|
28
30
|
|
29
31
|
# @return [Boolean] Whether or not the constant was defined
|
30
32
|
# before the current example.
|
@@ -50,6 +52,12 @@ module RSpec
|
|
50
52
|
@hidden
|
51
53
|
end
|
52
54
|
|
55
|
+
# @return [Boolean] Whether or not the provided constant name
|
56
|
+
# is a valid Ruby constant name.
|
57
|
+
def valid_name?
|
58
|
+
@valid_name
|
59
|
+
end
|
60
|
+
|
53
61
|
# The default `to_s` isn't very useful, so a custom version is provided.
|
54
62
|
def to_s
|
55
63
|
"#<#{self.class.name} #{name}>"
|
@@ -58,13 +66,16 @@ module RSpec
|
|
58
66
|
|
59
67
|
# @private
|
60
68
|
def self.unmutated(name)
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
const
|
69
|
+
previously_defined = recursive_const_defined?(name)
|
70
|
+
rescue NameError
|
71
|
+
new(name) do |c|
|
72
|
+
c.valid_name = false
|
73
|
+
end
|
74
|
+
else
|
75
|
+
new(name) do |const|
|
76
|
+
const.previously_defined = previously_defined
|
77
|
+
const.original_value = recursive_const_get(name) if previously_defined
|
78
|
+
end
|
68
79
|
end
|
69
80
|
|
70
81
|
# Queries rspec-mocks to find out information about the named constant.
|
@@ -6,8 +6,17 @@ module RSpec
|
|
6
6
|
# on the given argument.
|
7
7
|
def self.for(object_module_or_name, allow_direct_object_refs=false)
|
8
8
|
case object_module_or_name
|
9
|
-
when Module
|
10
|
-
|
9
|
+
when Module
|
10
|
+
if anonymous_module?(object_module_or_name)
|
11
|
+
DirectObjectReference.new(object_module_or_name)
|
12
|
+
else
|
13
|
+
# Use a `NamedObjectReference` if it has a name because this
|
14
|
+
# will use the original value of the constant in case it has
|
15
|
+
# been stubbed.
|
16
|
+
NamedObjectReference.new(name_of(object_module_or_name))
|
17
|
+
end
|
18
|
+
when String
|
19
|
+
NamedObjectReference.new(object_module_or_name)
|
11
20
|
else
|
12
21
|
if allow_direct_object_refs
|
13
22
|
DirectObjectReference.new(object_module_or_name)
|
@@ -17,74 +26,123 @@ module RSpec
|
|
17
26
|
end
|
18
27
|
end
|
19
28
|
end
|
29
|
+
|
30
|
+
if Module.new.name.nil?
|
31
|
+
def self.anonymous_module?(mod)
|
32
|
+
!name_of(mod)
|
33
|
+
end
|
34
|
+
else # 1.8.7
|
35
|
+
def self.anonymous_module?(mod)
|
36
|
+
name_of(mod) == ""
|
37
|
+
end
|
38
|
+
end
|
39
|
+
private_class_method :anonymous_module?
|
40
|
+
|
41
|
+
def self.name_of(mod)
|
42
|
+
MODULE_NAME_METHOD.bind(mod).call
|
43
|
+
end
|
44
|
+
private_class_method :name_of
|
45
|
+
|
46
|
+
# @private
|
47
|
+
MODULE_NAME_METHOD = Module.instance_method(:name)
|
20
48
|
end
|
21
49
|
|
22
|
-
#
|
50
|
+
# An implementation of rspec-mocks' reference interface.
|
51
|
+
# Used when an object is passed to {ExampleMethods#object_double}, or
|
52
|
+
# an anonymous class or module is passed to {ExampleMethods#instance_double}
|
53
|
+
# or {ExampleMethods#class_double}.
|
23
54
|
# Represents a reference to that object.
|
24
|
-
#
|
25
|
-
# @private
|
55
|
+
# @see NamedObjectReference
|
26
56
|
class DirectObjectReference
|
57
|
+
# @param object [Object] the object to which this refers
|
27
58
|
def initialize(object)
|
28
59
|
@object = object
|
29
60
|
end
|
30
61
|
|
62
|
+
# @return [String] the object's description (via `#inspect`).
|
31
63
|
def description
|
32
64
|
@object.inspect
|
33
65
|
end
|
34
66
|
|
67
|
+
# Defined for interface parity with the other object reference
|
68
|
+
# implementations. Raises an `ArgumentError` to indicate that `as_stubbed_const`
|
69
|
+
# is invalid when passing an object argument to `object_double`.
|
35
70
|
def const_to_replace
|
36
71
|
raise ArgumentError,
|
37
|
-
"Can not perform constant replacement with an object."
|
72
|
+
"Can not perform constant replacement with an anonymous object."
|
38
73
|
end
|
39
74
|
|
75
|
+
# The target of the verifying double (the object itself).
|
76
|
+
#
|
77
|
+
# @return [Object]
|
78
|
+
def target
|
79
|
+
@object
|
80
|
+
end
|
81
|
+
|
82
|
+
# Always returns true for an object as the class is defined.
|
83
|
+
#
|
84
|
+
# @return [true]
|
40
85
|
def defined?
|
41
86
|
true
|
42
87
|
end
|
43
88
|
|
89
|
+
# Yields if the reference target is loaded, providing a generic mechanism
|
90
|
+
# to optionally run a bit of code only when a reference's target is
|
91
|
+
# loaded.
|
92
|
+
#
|
93
|
+
# This specific implementation always yields because direct references
|
94
|
+
# are always loaded.
|
95
|
+
#
|
96
|
+
# @yield [Object] the target of this reference.
|
44
97
|
def when_loaded
|
45
98
|
yield @object
|
46
99
|
end
|
47
100
|
end
|
48
101
|
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
class DirectModuleReference < DirectObjectReference
|
54
|
-
def const_to_replace
|
55
|
-
@object.name
|
56
|
-
end
|
57
|
-
alias description const_to_replace
|
58
|
-
end
|
59
|
-
|
60
|
-
# Used when a string is passed to `class_double`, `instance_double`
|
61
|
-
# or `object_double`.
|
102
|
+
# An implementation of rspec-mocks' reference interface.
|
103
|
+
# Used when a string is passed to {ExampleMethods#object_double},
|
104
|
+
# and when a string, named class or named module is passed to
|
105
|
+
# {ExampleMethods#instance_double}, or {ExampleMethods#class_double}.
|
62
106
|
# Represents a reference to the object named (via a constant lookup)
|
63
107
|
# by the string.
|
64
|
-
#
|
65
|
-
# @private
|
108
|
+
# @see DirectObjectReference
|
66
109
|
class NamedObjectReference
|
110
|
+
# @param const_name [String] constant name
|
67
111
|
def initialize(const_name)
|
68
112
|
@const_name = const_name
|
69
113
|
end
|
70
114
|
|
115
|
+
# @return [Boolean] true if the named constant is defined, false otherwise.
|
71
116
|
def defined?
|
72
117
|
!!object
|
73
118
|
end
|
74
119
|
|
120
|
+
# @return [String] the constant name to replace with a double.
|
75
121
|
def const_to_replace
|
76
122
|
@const_name
|
77
123
|
end
|
78
124
|
alias description const_to_replace
|
79
125
|
|
80
|
-
|
126
|
+
# @return [Object, nil] the target of the verifying double (the named object), or
|
127
|
+
# nil if it is not defined.
|
128
|
+
def target
|
129
|
+
object
|
130
|
+
end
|
131
|
+
|
132
|
+
# Yields if the reference target is loaded, providing a generic mechanism
|
133
|
+
# to optionally run a bit of code only when a reference's target is
|
134
|
+
# loaded.
|
135
|
+
#
|
136
|
+
# @yield [Object] the target object
|
137
|
+
def when_loaded
|
81
138
|
yield object if object
|
82
139
|
end
|
83
140
|
|
84
141
|
private
|
85
142
|
|
86
143
|
def object
|
87
|
-
@object
|
144
|
+
return @object if defined?(@object)
|
145
|
+
@object = Constant.original(@const_name).original_value
|
88
146
|
end
|
89
147
|
end
|
90
148
|
end
|
data/lib/rspec/mocks/proxy.rb
CHANGED
@@ -88,7 +88,12 @@ module RSpec
|
|
88
88
|
@error_generator.raise_expectation_on_unstubbed_method(expected_method_name)
|
89
89
|
end
|
90
90
|
|
91
|
-
@messages_received.each do |(actual_method_name,
|
91
|
+
@messages_received.each do |(actual_method_name, received_arg_list, _)|
|
92
|
+
if expectation.message == actual_method_name
|
93
|
+
expectation.fail_if_problematic_received_arg_mutations(received_arg_list)
|
94
|
+
end
|
95
|
+
|
96
|
+
args = received_arg_list.args
|
92
97
|
next unless expectation.matches?(actual_method_name, *args)
|
93
98
|
|
94
99
|
expectation.safe_invoke(nil)
|
@@ -98,7 +103,8 @@ module RSpec
|
|
98
103
|
|
99
104
|
# @private
|
100
105
|
def check_for_unexpected_arguments(expectation)
|
101
|
-
@messages_received.each do |(method_name,
|
106
|
+
@messages_received.each do |(method_name, received_arg_list, _)|
|
107
|
+
args = received_arg_list.args
|
102
108
|
next unless expectation.matches_name_but_not_args(method_name, *args)
|
103
109
|
|
104
110
|
raise_unexpected_message_args_error(expectation, *args)
|
@@ -138,7 +144,11 @@ module RSpec
|
|
138
144
|
|
139
145
|
# @private
|
140
146
|
def received_message?(method_name, *args, &block)
|
141
|
-
@messages_received.any?
|
147
|
+
@messages_received.any? do |(received_method_name, received_arg_list, received_block)|
|
148
|
+
method_name == received_method_name &&
|
149
|
+
args == received_arg_list.args &&
|
150
|
+
block == received_block
|
151
|
+
end
|
142
152
|
end
|
143
153
|
|
144
154
|
# @private
|
@@ -149,7 +159,35 @@ module RSpec
|
|
149
159
|
# @private
|
150
160
|
def record_message_received(message, *args, &block)
|
151
161
|
@order_group.invoked SpecificMessage.new(object, message, args)
|
152
|
-
@messages_received << [message, args, block]
|
162
|
+
@messages_received << [message, ReceivedArgList.new(args), block]
|
163
|
+
end
|
164
|
+
|
165
|
+
class ReceivedArgList
|
166
|
+
attr_reader :args
|
167
|
+
|
168
|
+
def initialize(args)
|
169
|
+
@args = args
|
170
|
+
@original_hash = hash_of(args)
|
171
|
+
end
|
172
|
+
|
173
|
+
def has_mutations?
|
174
|
+
@original_hash != hash_of(args)
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def hash_of(arg)
|
180
|
+
arg.hash
|
181
|
+
rescue Exception
|
182
|
+
# While `Object#hash` is a built-in ruby method that we expect args to
|
183
|
+
# support, there's no guarantee that all args will. For example, a
|
184
|
+
# `BasicObject` instance will raise a `NoMethodError`. Given that
|
185
|
+
# we use the hash only to advise the user of a rare case we don't
|
186
|
+
# support involving mutations, it seems better to ignore this error
|
187
|
+
# and use a static value in its place (which will make us assume no
|
188
|
+
# mutation has occurred).
|
189
|
+
:failed_to_get_hash
|
190
|
+
end
|
153
191
|
end
|
154
192
|
|
155
193
|
# @private
|
@@ -28,6 +28,8 @@ module RSpec
|
|
28
28
|
else
|
29
29
|
__mock_proxy.ensure_publicly_implemented(message, self)
|
30
30
|
end
|
31
|
+
|
32
|
+
__mock_proxy.validate_arguments!(message, args)
|
31
33
|
end
|
32
34
|
|
33
35
|
super
|
@@ -47,8 +49,17 @@ module RSpec
|
|
47
49
|
__send__(name, *args, &block)
|
48
50
|
end
|
49
51
|
|
50
|
-
def initialize(*args)
|
51
|
-
|
52
|
+
def initialize(doubled_module, *args)
|
53
|
+
@doubled_module = doubled_module
|
54
|
+
|
55
|
+
possible_name = args.first
|
56
|
+
name = if String === possible_name || Symbol === possible_name
|
57
|
+
args.shift
|
58
|
+
else
|
59
|
+
@description
|
60
|
+
end
|
61
|
+
|
62
|
+
super(name, *args)
|
52
63
|
@__sending_message = nil
|
53
64
|
end
|
54
65
|
end
|
@@ -63,12 +74,8 @@ module RSpec
|
|
63
74
|
include VerifyingDouble
|
64
75
|
|
65
76
|
def initialize(doubled_module, *args)
|
66
|
-
@
|
67
|
-
|
68
|
-
super(
|
69
|
-
"#{doubled_module.description} (instance)",
|
70
|
-
*args
|
71
|
-
)
|
77
|
+
@description = "#{doubled_module.description} (instance)"
|
78
|
+
super
|
72
79
|
end
|
73
80
|
|
74
81
|
def __build_mock_proxy(order_group)
|
@@ -95,8 +102,8 @@ module RSpec
|
|
95
102
|
private
|
96
103
|
|
97
104
|
def initialize(doubled_module, *args)
|
98
|
-
@
|
99
|
-
super
|
105
|
+
@description = doubled_module.description
|
106
|
+
super
|
100
107
|
end
|
101
108
|
|
102
109
|
def __build_mock_proxy(order_group)
|
@@ -23,31 +23,23 @@ module RSpec
|
|
23
23
|
|
24
24
|
# @private
|
25
25
|
def with(*args, &block)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# No arguments given, this will raise.
|
33
|
-
super
|
34
|
-
end
|
35
|
-
|
36
|
-
validate_expected_arguments!(expected_args)
|
26
|
+
super(*args, &block).tap do
|
27
|
+
validate_expected_arguments! do |signature|
|
28
|
+
example_call_site_args = [:an_arg] * signature.min_non_kw_args
|
29
|
+
example_call_site_args << :kw_args_hash if signature.required_kw_args.any?
|
30
|
+
@argument_list_matcher.resolve_expected_args_based_on(example_call_site_args)
|
31
|
+
end
|
37
32
|
end
|
38
|
-
super
|
39
33
|
end
|
40
34
|
|
41
35
|
private
|
42
36
|
|
43
|
-
def validate_expected_arguments!
|
37
|
+
def validate_expected_arguments!
|
44
38
|
return if method_reference.nil?
|
45
39
|
|
46
40
|
method_reference.with_signature do |signature|
|
47
|
-
|
48
|
-
|
49
|
-
actual_args
|
50
|
-
)
|
41
|
+
args = yield signature
|
42
|
+
verifier = Support::LooseSignatureVerifier.new(signature, args)
|
51
43
|
|
52
44
|
unless verifier.valid?
|
53
45
|
# Fail fast is required, otherwise the message expecation will fail
|
@@ -78,6 +78,10 @@ module RSpec
|
|
78
78
|
def visibility_for(method_name)
|
79
79
|
method_reference[method_name].visibility
|
80
80
|
end
|
81
|
+
|
82
|
+
def validate_arguments!(method_name, args)
|
83
|
+
@method_doubles[method_name].validate_arguments!(args)
|
84
|
+
end
|
81
85
|
end
|
82
86
|
|
83
87
|
# @private
|
@@ -131,8 +135,6 @@ module RSpec
|
|
131
135
|
super
|
132
136
|
end
|
133
137
|
|
134
|
-
private
|
135
|
-
|
136
138
|
def validate_arguments!(actual_args)
|
137
139
|
@method_reference.with_signature do |signature|
|
138
140
|
verifier = Support::StrictSignatureVerifier.new(signature, actual_args)
|