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.
Files changed (49) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +1 -1
  5. data/Changelog.md +512 -2
  6. data/{License.txt → LICENSE.md} +5 -4
  7. data/README.md +113 -30
  8. data/lib/rspec/mocks/any_instance/chain.rb +5 -3
  9. data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
  10. data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +1 -5
  11. data/lib/rspec/mocks/any_instance/expectation_chain.rb +9 -8
  12. data/lib/rspec/mocks/any_instance/message_chains.rb +7 -8
  13. data/lib/rspec/mocks/any_instance/proxy.rb +14 -5
  14. data/lib/rspec/mocks/any_instance/recorder.rb +61 -31
  15. data/lib/rspec/mocks/any_instance/stub_chain.rb +15 -11
  16. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +1 -5
  17. data/lib/rspec/mocks/any_instance.rb +1 -0
  18. data/lib/rspec/mocks/argument_list_matcher.rb +55 -10
  19. data/lib/rspec/mocks/argument_matchers.rb +88 -30
  20. data/lib/rspec/mocks/configuration.rb +61 -13
  21. data/lib/rspec/mocks/error_generator.rb +250 -107
  22. data/lib/rspec/mocks/example_methods.rb +151 -28
  23. data/lib/rspec/mocks/instance_method_stasher.rb +17 -6
  24. data/lib/rspec/mocks/matchers/have_received.rb +50 -20
  25. data/lib/rspec/mocks/matchers/receive.rb +39 -11
  26. data/lib/rspec/mocks/matchers/receive_message_chain.rb +22 -7
  27. data/lib/rspec/mocks/matchers/receive_messages.rb +12 -7
  28. data/lib/rspec/mocks/message_chain.rb +3 -7
  29. data/lib/rspec/mocks/message_expectation.rb +466 -307
  30. data/lib/rspec/mocks/method_double.rb +88 -29
  31. data/lib/rspec/mocks/method_reference.rb +85 -25
  32. data/lib/rspec/mocks/minitest_integration.rb +68 -0
  33. data/lib/rspec/mocks/mutate_const.rb +50 -109
  34. data/lib/rspec/mocks/object_reference.rb +89 -32
  35. data/lib/rspec/mocks/order_group.rb +4 -5
  36. data/lib/rspec/mocks/proxy.rb +156 -60
  37. data/lib/rspec/mocks/space.rb +52 -35
  38. data/lib/rspec/mocks/standalone.rb +1 -1
  39. data/lib/rspec/mocks/syntax.rb +26 -30
  40. data/lib/rspec/mocks/targets.rb +55 -28
  41. data/lib/rspec/mocks/test_double.rb +43 -7
  42. data/lib/rspec/mocks/verifying_double.rb +27 -33
  43. data/lib/rspec/mocks/{verifying_message_expecation.rb → verifying_message_expectation.rb} +11 -16
  44. data/lib/rspec/mocks/verifying_proxy.rb +77 -26
  45. data/lib/rspec/mocks/version.rb +1 -1
  46. data/lib/rspec/mocks.rb +8 -1
  47. data.tar.gz.sig +0 -0
  48. metadata +80 -43
  49. 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
- const = new(name)
136
- const.previously_defined = recursive_const_defined?(name)
137
- const.stubbed = false
138
- const.hidden = false
139
- const.original_value = recursive_const_get(name) if const.previously_defined?
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 contaning information about the named
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
- mutator = if recursive_const_defined?(constant_name, &raise_on_invalid_const)
172
- DefinedConstantReplacer
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
- unless value.respond_to?(:constants)
302
- raise ArgumentError,
303
- "Cannot transfer nested constants for #{@full_constant_name} " +
304
- "since #{description} is not a class or module and only classes " +
305
- "and modules support nested constants."
306
- end
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
- "Cannot transfer nested constant(s) #{undefined_constants.join(' and ')} " +
317
- "for #{@full_constant_name} since they are not defined. Did you mean " +
318
- "#{available_constants.join(' or ')}?"
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
- else
368
- parent.name
369
- end
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 = false)
7
+ def self.for(object_module_or_name, allow_direct_object_refs=false)
9
8
  case object_module_or_name
10
- when Module then DirectModuleReference.new(object_module_or_name)
11
- when String then NamedObjectReference.new(object_module_or_name)
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
- if allow_direct_object_refs
14
- DirectObjectReference.new(object_module_or_name)
15
- else
16
- raise ArgumentError,
17
- "Module or String expected, got #{object_module_or_name.inspect}"
18
- end
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
- # Used when an object is passed to `object_double`.
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
- "Can not perform constant replacement with an object."
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
- # Used when a module is passed to `class_double` or `instance_double`.
51
- # Represents a reference to that module.
52
- #
53
- # @private
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
- def when_loaded(&block)
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
- private
141
+ private
86
142
 
87
143
  def object
88
- @object ||= Constant.original(@const_name).original_value
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
- if expectation.ordered?
29
- @index += index + 1
30
- return expectation
31
- end
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