rspec-mocks 3.0.4 → 3.12.6
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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +1 -1
- data/Changelog.md +512 -2
- data/{License.txt → LICENSE.md} +5 -4
- data/README.md +113 -30
- data/lib/rspec/mocks/any_instance/chain.rb +5 -3
- data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
- data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +1 -5
- data/lib/rspec/mocks/any_instance/expectation_chain.rb +9 -8
- data/lib/rspec/mocks/any_instance/message_chains.rb +7 -8
- data/lib/rspec/mocks/any_instance/proxy.rb +14 -5
- data/lib/rspec/mocks/any_instance/recorder.rb +61 -31
- data/lib/rspec/mocks/any_instance/stub_chain.rb +15 -11
- data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +1 -5
- data/lib/rspec/mocks/any_instance.rb +1 -0
- data/lib/rspec/mocks/argument_list_matcher.rb +55 -10
- data/lib/rspec/mocks/argument_matchers.rb +88 -30
- data/lib/rspec/mocks/configuration.rb +61 -13
- data/lib/rspec/mocks/error_generator.rb +250 -107
- data/lib/rspec/mocks/example_methods.rb +151 -28
- data/lib/rspec/mocks/instance_method_stasher.rb +17 -6
- data/lib/rspec/mocks/matchers/have_received.rb +50 -20
- data/lib/rspec/mocks/matchers/receive.rb +39 -11
- data/lib/rspec/mocks/matchers/receive_message_chain.rb +22 -7
- data/lib/rspec/mocks/matchers/receive_messages.rb +12 -7
- data/lib/rspec/mocks/message_chain.rb +3 -7
- data/lib/rspec/mocks/message_expectation.rb +466 -307
- data/lib/rspec/mocks/method_double.rb +88 -29
- data/lib/rspec/mocks/method_reference.rb +85 -25
- data/lib/rspec/mocks/minitest_integration.rb +68 -0
- data/lib/rspec/mocks/mutate_const.rb +50 -109
- data/lib/rspec/mocks/object_reference.rb +89 -32
- data/lib/rspec/mocks/order_group.rb +4 -5
- data/lib/rspec/mocks/proxy.rb +156 -60
- data/lib/rspec/mocks/space.rb +52 -35
- data/lib/rspec/mocks/standalone.rb +1 -1
- data/lib/rspec/mocks/syntax.rb +26 -30
- data/lib/rspec/mocks/targets.rb +55 -28
- data/lib/rspec/mocks/test_double.rb +43 -7
- data/lib/rspec/mocks/verifying_double.rb +27 -33
- data/lib/rspec/mocks/{verifying_message_expecation.rb → verifying_message_expectation.rb} +11 -16
- data/lib/rspec/mocks/verifying_proxy.rb +77 -26
- data/lib/rspec/mocks/version.rb +1 -1
- data/lib/rspec/mocks.rb +8 -1
- data.tar.gz.sig +0 -0
- metadata +80 -43
- metadata.gz.sig +0 -0
@@ -1,85 +1,11 @@
|
|
1
|
+
RSpec::Support.require_rspec_support 'recursive_const_methods'
|
2
|
+
|
1
3
|
module RSpec
|
2
4
|
module Mocks
|
3
|
-
# Provides recursive constant lookup methods useful for
|
4
|
-
# constant stubbing.
|
5
|
-
#
|
6
|
-
# @private
|
7
|
-
module RecursiveConstMethods
|
8
|
-
# We only want to consider constants that are defined directly on a
|
9
|
-
# particular module, and not include top-level/inherited constants.
|
10
|
-
# Unfortunately, the constant API changed between 1.8 and 1.9, so
|
11
|
-
# we need to conditionally define methods to ignore the top-level/inherited
|
12
|
-
# constants.
|
13
|
-
#
|
14
|
-
# Given:
|
15
|
-
# class A; B = 1; end
|
16
|
-
# class C < A; end
|
17
|
-
#
|
18
|
-
# On 1.8:
|
19
|
-
# - C.const_get("Hash") # => ::Hash
|
20
|
-
# - C.const_defined?("Hash") # => false
|
21
|
-
# - C.constants # => ["B"]
|
22
|
-
# - None of these methods accept the extra `inherit` argument
|
23
|
-
# On 1.9:
|
24
|
-
# - C.const_get("Hash") # => ::Hash
|
25
|
-
# - C.const_defined?("Hash") # => true
|
26
|
-
# - C.const_get("Hash", false) # => raises NameError
|
27
|
-
# - C.const_defined?("Hash", false) # => false
|
28
|
-
# - C.constants # => [:B]
|
29
|
-
# - C.constants(false) #=> []
|
30
|
-
if Module.method(:const_defined?).arity == 1
|
31
|
-
def const_defined_on?(mod, const_name)
|
32
|
-
mod.const_defined?(const_name)
|
33
|
-
end
|
34
|
-
|
35
|
-
def get_const_defined_on(mod, const_name)
|
36
|
-
if const_defined_on?(mod, const_name)
|
37
|
-
return mod.const_get(const_name)
|
38
|
-
end
|
39
|
-
|
40
|
-
raise NameError, "uninitialized constant #{mod.name}::#{const_name}"
|
41
|
-
end
|
42
|
-
|
43
|
-
def constants_defined_on(mod)
|
44
|
-
mod.constants.select { |c| const_defined_on?(mod, c) }
|
45
|
-
end
|
46
|
-
else
|
47
|
-
def const_defined_on?(mod, const_name)
|
48
|
-
mod.const_defined?(const_name, false)
|
49
|
-
end
|
50
|
-
|
51
|
-
def get_const_defined_on(mod, const_name)
|
52
|
-
mod.const_get(const_name, false)
|
53
|
-
end
|
54
|
-
|
55
|
-
def constants_defined_on(mod)
|
56
|
-
mod.constants(false)
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def recursive_const_get(const_name)
|
61
|
-
normalize_const_name(const_name).split('::').inject(Object) do |mod, name|
|
62
|
-
get_const_defined_on(mod, name)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def recursive_const_defined?(const_name)
|
67
|
-
normalize_const_name(const_name).split('::').inject([Object, '']) do |(mod, full_name), name|
|
68
|
-
yield(full_name, name) if block_given? && !(Module === mod)
|
69
|
-
return false unless const_defined_on?(mod, name)
|
70
|
-
[get_const_defined_on(mod, name), [mod, name].join('::')]
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def normalize_const_name(const_name)
|
75
|
-
const_name.sub(/\A::/, '')
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
5
|
# Provides information about constants that may (or may not)
|
80
6
|
# have been mutated by rspec-mocks.
|
81
7
|
class Constant
|
82
|
-
extend RecursiveConstMethods
|
8
|
+
extend Support::RecursiveConstMethods
|
83
9
|
|
84
10
|
# @api private
|
85
11
|
def initialize(name)
|
@@ -87,6 +13,8 @@ module RSpec
|
|
87
13
|
@previously_defined = false
|
88
14
|
@stubbed = false
|
89
15
|
@hidden = false
|
16
|
+
@valid_name = true
|
17
|
+
yield self if block_given?
|
90
18
|
end
|
91
19
|
|
92
20
|
# @return [String] The fully qualified name of the constant.
|
@@ -98,7 +26,7 @@ module RSpec
|
|
98
26
|
attr_accessor :original_value
|
99
27
|
|
100
28
|
# @private
|
101
|
-
attr_writer :previously_defined, :stubbed, :hidden
|
29
|
+
attr_writer :previously_defined, :stubbed, :hidden, :valid_name
|
102
30
|
|
103
31
|
# @return [Boolean] Whether or not the constant was defined
|
104
32
|
# before the current example.
|
@@ -124,6 +52,12 @@ module RSpec
|
|
124
52
|
@hidden
|
125
53
|
end
|
126
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
|
+
|
127
61
|
# The default `to_s` isn't very useful, so a custom version is provided.
|
128
62
|
def to_s
|
129
63
|
"#<#{self.class.name} #{name}>"
|
@@ -132,19 +66,22 @@ module RSpec
|
|
132
66
|
|
133
67
|
# @private
|
134
68
|
def self.unmutated(name)
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
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
|
142
79
|
end
|
143
80
|
|
144
81
|
# Queries rspec-mocks to find out information about the named constant.
|
145
82
|
#
|
146
83
|
# @param [String] name the name of the constant
|
147
|
-
# @return [Constant] an object
|
84
|
+
# @return [Constant] an object containing information about the named
|
148
85
|
# constant.
|
149
86
|
def self.original(name)
|
150
87
|
mutator = ::RSpec::Mocks.space.constant_mutator_for(name)
|
@@ -154,7 +91,7 @@ module RSpec
|
|
154
91
|
|
155
92
|
# Provides a means to stub constants.
|
156
93
|
class ConstantMutator
|
157
|
-
extend RecursiveConstMethods
|
94
|
+
extend Support::RecursiveConstMethods
|
158
95
|
|
159
96
|
# Stubs a constant.
|
160
97
|
#
|
@@ -167,13 +104,17 @@ module RSpec
|
|
167
104
|
# examples. This is an alternate public API that is provided
|
168
105
|
# so you can stub constants in other contexts (e.g. helper
|
169
106
|
# classes).
|
170
|
-
def self.stub(constant_name, value, options
|
171
|
-
|
172
|
-
|
173
|
-
else
|
174
|
-
UndefinedConstantSetter
|
107
|
+
def self.stub(constant_name, value, options={})
|
108
|
+
unless String === constant_name
|
109
|
+
raise ArgumentError, "`stub_const` requires a String, but you provided a #{constant_name.class.name}"
|
175
110
|
end
|
176
111
|
|
112
|
+
mutator = if recursive_const_defined?(constant_name, &raise_on_invalid_const)
|
113
|
+
DefinedConstantReplacer
|
114
|
+
else
|
115
|
+
UndefinedConstantSetter
|
116
|
+
end
|
117
|
+
|
177
118
|
mutate(mutator.new(constant_name, value, options[:transfer_nested_constants]))
|
178
119
|
value
|
179
120
|
end
|
@@ -188,7 +129,7 @@ module RSpec
|
|
188
129
|
# so you can hide constants in other contexts (e.g. helper
|
189
130
|
# classes).
|
190
131
|
def self.hide(constant_name)
|
191
|
-
mutate(ConstantHider.new(constant_name, nil, {
|
132
|
+
mutate(ConstantHider.new(constant_name, nil, {}))
|
192
133
|
nil
|
193
134
|
end
|
194
135
|
|
@@ -196,7 +137,7 @@ module RSpec
|
|
196
137
|
#
|
197
138
|
# @private
|
198
139
|
class BaseMutator
|
199
|
-
include RecursiveConstMethods
|
140
|
+
include Support::RecursiveConstMethods
|
200
141
|
|
201
142
|
attr_reader :original_value, :full_constant_name
|
202
143
|
|
@@ -227,7 +168,7 @@ module RSpec
|
|
227
168
|
# @private
|
228
169
|
class ConstantHider < BaseMutator
|
229
170
|
def mutate
|
230
|
-
return unless @defined = recursive_const_defined?(full_constant_name)
|
171
|
+
return unless (@defined = recursive_const_defined?(full_constant_name))
|
231
172
|
@context = recursive_const_get(@context_parts.join('::'))
|
232
173
|
@original_value = get_const_defined_on(@context, @const_name)
|
233
174
|
|
@@ -298,12 +239,12 @@ module RSpec
|
|
298
239
|
return [] unless should_transfer_nested_constants?
|
299
240
|
|
300
241
|
{ @original_value => "the original value", @mutated_value => "the stubbed value" }.each do |value, description|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
242
|
+
next if value.respond_to?(:constants)
|
243
|
+
|
244
|
+
raise ArgumentError,
|
245
|
+
"Cannot transfer nested constants for #{@full_constant_name} " \
|
246
|
+
"since #{description} is not a class or module and only classes " \
|
247
|
+
"and modules support nested constants."
|
307
248
|
end
|
308
249
|
|
309
250
|
if Array === @transfer_nested_constants
|
@@ -313,9 +254,9 @@ module RSpec
|
|
313
254
|
if undefined_constants.any?
|
314
255
|
available_constants = constants_defined_on(@original_value) - @transfer_nested_constants
|
315
256
|
raise ArgumentError,
|
316
|
-
|
317
|
-
|
318
|
-
|
257
|
+
"Cannot transfer nested constant(s) #{undefined_constants.join(' and ')} " \
|
258
|
+
"for #{@full_constant_name} since they are not defined. Did you mean " \
|
259
|
+
"#{available_constants.join(' or ')}?"
|
319
260
|
end
|
320
261
|
|
321
262
|
@transfer_nested_constants
|
@@ -363,10 +304,10 @@ module RSpec
|
|
363
304
|
|
364
305
|
def name_for(parent, name)
|
365
306
|
root = if parent == Object
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
307
|
+
''
|
308
|
+
else
|
309
|
+
parent.name
|
310
|
+
end
|
370
311
|
root + '::' + name
|
371
312
|
end
|
372
313
|
end
|
@@ -389,7 +330,7 @@ module RSpec
|
|
389
330
|
# @api private
|
390
331
|
def self.raise_on_invalid_const
|
391
332
|
lambda do |const_name, failed_name|
|
392
|
-
raise "Cannot stub constant #{failed_name} on #{const_name} "
|
333
|
+
raise "Cannot stub constant #{failed_name} on #{const_name} " \
|
393
334
|
"since #{const_name} is not a module."
|
394
335
|
end
|
395
336
|
end
|
@@ -1,91 +1,148 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Mocks
|
3
|
-
|
4
3
|
# @private
|
5
4
|
class ObjectReference
|
6
5
|
# Returns an appropriate Object or Module reference based
|
7
6
|
# on the given argument.
|
8
|
-
def self.for(object_module_or_name, allow_direct_object_refs
|
7
|
+
def self.for(object_module_or_name, allow_direct_object_refs=false)
|
9
8
|
case object_module_or_name
|
10
|
-
|
11
|
-
|
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)
|
20
|
+
else
|
21
|
+
if allow_direct_object_refs
|
22
|
+
DirectObjectReference.new(object_module_or_name)
|
12
23
|
else
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
24
|
+
raise ArgumentError,
|
25
|
+
"Module or String expected, got #{object_module_or_name.inspect}"
|
26
|
+
end
|
27
|
+
end
|
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) == ""
|
19
37
|
end
|
20
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)
|
21
48
|
end
|
22
49
|
|
23
|
-
#
|
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}.
|
24
54
|
# Represents a reference to that object.
|
25
|
-
#
|
26
|
-
# @private
|
55
|
+
# @see NamedObjectReference
|
27
56
|
class DirectObjectReference
|
57
|
+
# @param object [Object] the object to which this refers
|
28
58
|
def initialize(object)
|
29
59
|
@object = object
|
30
60
|
end
|
31
61
|
|
62
|
+
# @return [String] the object's description (via `#inspect`).
|
32
63
|
def description
|
33
64
|
@object.inspect
|
34
65
|
end
|
35
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`.
|
36
70
|
def const_to_replace
|
37
71
|
raise ArgumentError,
|
38
|
-
|
72
|
+
"Can not perform constant replacement with an anonymous object."
|
73
|
+
end
|
74
|
+
|
75
|
+
# The target of the verifying double (the object itself).
|
76
|
+
#
|
77
|
+
# @return [Object]
|
78
|
+
def target
|
79
|
+
@object
|
39
80
|
end
|
40
81
|
|
82
|
+
# Always returns true for an object as the class is defined.
|
83
|
+
#
|
84
|
+
# @return [true]
|
41
85
|
def defined?
|
42
86
|
true
|
43
87
|
end
|
44
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.
|
45
97
|
def when_loaded
|
46
98
|
yield @object
|
47
99
|
end
|
48
100
|
end
|
49
101
|
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
class DirectModuleReference < DirectObjectReference
|
55
|
-
def const_to_replace
|
56
|
-
@object.name
|
57
|
-
end
|
58
|
-
alias description const_to_replace
|
59
|
-
end
|
60
|
-
|
61
|
-
# Used when a string is passed to `class_double`, `instance_double`
|
62
|
-
# 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}.
|
63
106
|
# Represents a reference to the object named (via a constant lookup)
|
64
107
|
# by the string.
|
65
|
-
#
|
66
|
-
# @private
|
108
|
+
# @see DirectObjectReference
|
67
109
|
class NamedObjectReference
|
110
|
+
# @param const_name [String] constant name
|
68
111
|
def initialize(const_name)
|
69
112
|
@const_name = const_name
|
70
113
|
end
|
71
114
|
|
115
|
+
# @return [Boolean] true if the named constant is defined, false otherwise.
|
72
116
|
def defined?
|
73
117
|
!!object
|
74
118
|
end
|
75
119
|
|
120
|
+
# @return [String] the constant name to replace with a double.
|
76
121
|
def const_to_replace
|
77
122
|
@const_name
|
78
123
|
end
|
79
124
|
alias description const_to_replace
|
80
125
|
|
81
|
-
|
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
|
82
138
|
yield object if object
|
83
139
|
end
|
84
140
|
|
85
|
-
|
141
|
+
private
|
86
142
|
|
87
143
|
def object
|
88
|
-
@object
|
144
|
+
return @object if defined?(@object)
|
145
|
+
@object = Constant.original(@const_name).original_value
|
89
146
|
end
|
90
147
|
end
|
91
148
|
end
|
@@ -25,10 +25,10 @@ module RSpec
|
|
25
25
|
# @private
|
26
26
|
def consume
|
27
27
|
remaining_expectations.each_with_index do |expectation, index|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
next unless expectation.ordered?
|
29
|
+
|
30
|
+
@index += index + 1
|
31
|
+
return expectation
|
32
32
|
end
|
33
33
|
nil
|
34
34
|
end
|
@@ -76,7 +76,6 @@ module RSpec
|
|
76
76
|
def expectation_for(message)
|
77
77
|
@expectations.find { |e| message == e }
|
78
78
|
end
|
79
|
-
|
80
79
|
end
|
81
80
|
end
|
82
81
|
end
|