rspec-mocks-diag 3.8.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.yardopts +6 -0
  4. data/Changelog.md +1108 -0
  5. data/LICENSE.md +25 -0
  6. data/README.md +460 -0
  7. data/lib/rspec/mocks.rb +130 -0
  8. data/lib/rspec/mocks/any_instance.rb +11 -0
  9. data/lib/rspec/mocks/any_instance/chain.rb +110 -0
  10. data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
  11. data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +31 -0
  12. data/lib/rspec/mocks/any_instance/expectation_chain.rb +50 -0
  13. data/lib/rspec/mocks/any_instance/message_chains.rb +83 -0
  14. data/lib/rspec/mocks/any_instance/proxy.rb +116 -0
  15. data/lib/rspec/mocks/any_instance/recorder.rb +289 -0
  16. data/lib/rspec/mocks/any_instance/stub_chain.rb +51 -0
  17. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +23 -0
  18. data/lib/rspec/mocks/argument_list_matcher.rb +100 -0
  19. data/lib/rspec/mocks/argument_matchers.rb +320 -0
  20. data/lib/rspec/mocks/configuration.rb +212 -0
  21. data/lib/rspec/mocks/error_generator.rb +378 -0
  22. data/lib/rspec/mocks/example_methods.rb +434 -0
  23. data/lib/rspec/mocks/instance_method_stasher.rb +146 -0
  24. data/lib/rspec/mocks/marshal_extension.rb +41 -0
  25. data/lib/rspec/mocks/matchers/expectation_customization.rb +20 -0
  26. data/lib/rspec/mocks/matchers/have_received.rb +134 -0
  27. data/lib/rspec/mocks/matchers/receive.rb +132 -0
  28. data/lib/rspec/mocks/matchers/receive_message_chain.rb +82 -0
  29. data/lib/rspec/mocks/matchers/receive_messages.rb +77 -0
  30. data/lib/rspec/mocks/message_chain.rb +87 -0
  31. data/lib/rspec/mocks/message_expectation.rb +748 -0
  32. data/lib/rspec/mocks/method_double.rb +287 -0
  33. data/lib/rspec/mocks/method_reference.rb +202 -0
  34. data/lib/rspec/mocks/minitest_integration.rb +68 -0
  35. data/lib/rspec/mocks/mutate_const.rb +339 -0
  36. data/lib/rspec/mocks/object_reference.rb +149 -0
  37. data/lib/rspec/mocks/order_group.rb +81 -0
  38. data/lib/rspec/mocks/proxy.rb +485 -0
  39. data/lib/rspec/mocks/space.rb +238 -0
  40. data/lib/rspec/mocks/standalone.rb +3 -0
  41. data/lib/rspec/mocks/syntax.rb +325 -0
  42. data/lib/rspec/mocks/targets.rb +124 -0
  43. data/lib/rspec/mocks/test_double.rb +171 -0
  44. data/lib/rspec/mocks/verifying_double.rb +129 -0
  45. data/lib/rspec/mocks/verifying_message_expectation.rb +54 -0
  46. data/lib/rspec/mocks/verifying_proxy.rb +220 -0
  47. data/lib/rspec/mocks/version.rb +9 -0
  48. metadata +186 -0
@@ -0,0 +1,68 @@
1
+ require 'rspec/mocks'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ # @private
6
+ module MinitestIntegration
7
+ include ::RSpec::Mocks::ExampleMethods
8
+
9
+ def before_setup
10
+ ::RSpec::Mocks.setup
11
+ super
12
+ end
13
+
14
+ def after_teardown
15
+ super
16
+
17
+ # Only verify if there's not already an error. Otherwise
18
+ # we risk getting the same failure twice, since negative
19
+ # expectation violations raise both when the message is
20
+ # unexpectedly received, and also during `verify` (in case
21
+ # the first failure was caught by user code via a
22
+ # `rescue Exception`).
23
+ ::RSpec::Mocks.verify unless failures.any?
24
+ ensure
25
+ ::RSpec::Mocks.teardown
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ Minitest::Test.send(:include, RSpec::Mocks::MinitestIntegration)
32
+
33
+ if defined?(::Minitest::Expectation)
34
+ if defined?(::RSpec::Expectations) && ::Minitest::Expectation.method_defined?(:to)
35
+ # rspec/expectations/minitest_integration has already been loaded and
36
+ # has defined `to`/`not_to`/`to_not` on `Minitest::Expectation` so we do
37
+ # not want to here (or else we would interfere with rspec-expectations' definition).
38
+ else
39
+ # ...otherwise, define those methods now. If `rspec/expectations/minitest_integration`
40
+ # is loaded after this file, it'll overide the defintion here.
41
+ Minitest::Expectation.class_eval do
42
+ include RSpec::Mocks::ExpectationTargetMethods
43
+
44
+ def to(*args)
45
+ ctx.assertions += 1
46
+ super
47
+ end
48
+
49
+ def not_to(*args)
50
+ ctx.assertions += 1
51
+ super
52
+ end
53
+
54
+ def to_not(*args)
55
+ ctx.assertions += 1
56
+ super
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ module RSpec
63
+ module Mocks
64
+ remove_const :MockExpectationError
65
+ # Raised when a message expectation is not satisfied.
66
+ MockExpectationError = ::Minitest::Assertion
67
+ end
68
+ end
@@ -0,0 +1,339 @@
1
+ RSpec::Support.require_rspec_support 'recursive_const_methods'
2
+
3
+ module RSpec
4
+ module Mocks
5
+ # Provides information about constants that may (or may not)
6
+ # have been mutated by rspec-mocks.
7
+ class Constant
8
+ extend Support::RecursiveConstMethods
9
+
10
+ # @api private
11
+ def initialize(name)
12
+ @name = name
13
+ @previously_defined = false
14
+ @stubbed = false
15
+ @hidden = false
16
+ @valid_name = true
17
+ yield self if block_given?
18
+ end
19
+
20
+ # @return [String] The fully qualified name of the constant.
21
+ attr_reader :name
22
+
23
+ # @return [Object, nil] The original value (e.g. before it
24
+ # was mutated by rspec-mocks) of the constant, or
25
+ # nil if the constant was not previously defined.
26
+ attr_accessor :original_value
27
+
28
+ # @private
29
+ attr_writer :previously_defined, :stubbed, :hidden, :valid_name
30
+
31
+ # @return [Boolean] Whether or not the constant was defined
32
+ # before the current example.
33
+ def previously_defined?
34
+ @previously_defined
35
+ end
36
+
37
+ # @return [Boolean] Whether or not rspec-mocks has mutated
38
+ # (stubbed or hidden) this constant.
39
+ def mutated?
40
+ @stubbed || @hidden
41
+ end
42
+
43
+ # @return [Boolean] Whether or not rspec-mocks has stubbed
44
+ # this constant.
45
+ def stubbed?
46
+ @stubbed
47
+ end
48
+
49
+ # @return [Boolean] Whether or not rspec-mocks has hidden
50
+ # this constant.
51
+ def hidden?
52
+ @hidden
53
+ end
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
+
61
+ # The default `to_s` isn't very useful, so a custom version is provided.
62
+ def to_s
63
+ "#<#{self.class.name} #{name}>"
64
+ end
65
+ alias inspect to_s
66
+
67
+ # @private
68
+ def self.unmutated(name)
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
79
+ end
80
+
81
+ # Queries rspec-mocks to find out information about the named constant.
82
+ #
83
+ # @param [String] name the name of the constant
84
+ # @return [Constant] an object contaning information about the named
85
+ # constant.
86
+ def self.original(name)
87
+ mutator = ::RSpec::Mocks.space.constant_mutator_for(name)
88
+ mutator ? mutator.to_constant : unmutated(name)
89
+ end
90
+ end
91
+
92
+ # Provides a means to stub constants.
93
+ class ConstantMutator
94
+ extend Support::RecursiveConstMethods
95
+
96
+ # Stubs a constant.
97
+ #
98
+ # @param (see ExampleMethods#stub_const)
99
+ # @option (see ExampleMethods#stub_const)
100
+ # @return (see ExampleMethods#stub_const)
101
+ #
102
+ # @see ExampleMethods#stub_const
103
+ # @note It's recommended that you use `stub_const` in your
104
+ # examples. This is an alternate public API that is provided
105
+ # so you can stub constants in other contexts (e.g. helper
106
+ # classes).
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}"
110
+ end
111
+
112
+ mutator = if recursive_const_defined?(constant_name, &raise_on_invalid_const)
113
+ DefinedConstantReplacer
114
+ else
115
+ UndefinedConstantSetter
116
+ end
117
+
118
+ mutate(mutator.new(constant_name, value, options[:transfer_nested_constants]))
119
+ value
120
+ end
121
+
122
+ # Hides a constant.
123
+ #
124
+ # @param (see ExampleMethods#hide_const)
125
+ #
126
+ # @see ExampleMethods#hide_const
127
+ # @note It's recommended that you use `hide_const` in your
128
+ # examples. This is an alternate public API that is provided
129
+ # so you can hide constants in other contexts (e.g. helper
130
+ # classes).
131
+ def self.hide(constant_name)
132
+ mutate(ConstantHider.new(constant_name, nil, {}))
133
+ nil
134
+ end
135
+
136
+ # Contains common functionality used by all of the constant mutators.
137
+ #
138
+ # @private
139
+ class BaseMutator
140
+ include Support::RecursiveConstMethods
141
+
142
+ attr_reader :original_value, :full_constant_name
143
+
144
+ def initialize(full_constant_name, mutated_value, transfer_nested_constants)
145
+ @full_constant_name = normalize_const_name(full_constant_name)
146
+ @mutated_value = mutated_value
147
+ @transfer_nested_constants = transfer_nested_constants
148
+ @context_parts = @full_constant_name.split('::')
149
+ @const_name = @context_parts.pop
150
+ @reset_performed = false
151
+ end
152
+
153
+ def to_constant
154
+ const = Constant.new(full_constant_name)
155
+ const.original_value = original_value
156
+
157
+ const
158
+ end
159
+
160
+ def idempotently_reset
161
+ reset unless @reset_performed
162
+ @reset_performed = true
163
+ end
164
+ end
165
+
166
+ # Hides a defined constant for the duration of an example.
167
+ #
168
+ # @private
169
+ class ConstantHider < BaseMutator
170
+ def mutate
171
+ return unless (@defined = recursive_const_defined?(full_constant_name))
172
+ @context = recursive_const_get(@context_parts.join('::'))
173
+ @original_value = get_const_defined_on(@context, @const_name)
174
+
175
+ @context.__send__(:remove_const, @const_name)
176
+ end
177
+
178
+ def to_constant
179
+ return Constant.unmutated(full_constant_name) unless @defined
180
+
181
+ const = super
182
+ const.hidden = true
183
+ const.previously_defined = true
184
+
185
+ const
186
+ end
187
+
188
+ def reset
189
+ return unless @defined
190
+ @context.const_set(@const_name, @original_value)
191
+ end
192
+ end
193
+
194
+ # Replaces a defined constant for the duration of an example.
195
+ #
196
+ # @private
197
+ class DefinedConstantReplacer < BaseMutator
198
+ def initialize(*args)
199
+ super
200
+ @constants_to_transfer = []
201
+ end
202
+
203
+ def mutate
204
+ @context = recursive_const_get(@context_parts.join('::'))
205
+ @original_value = get_const_defined_on(@context, @const_name)
206
+
207
+ @constants_to_transfer = verify_constants_to_transfer!
208
+
209
+ @context.__send__(:remove_const, @const_name)
210
+ @context.const_set(@const_name, @mutated_value)
211
+
212
+ transfer_nested_constants
213
+ end
214
+
215
+ def to_constant
216
+ const = super
217
+ const.stubbed = true
218
+ const.previously_defined = true
219
+
220
+ const
221
+ end
222
+
223
+ def reset
224
+ @constants_to_transfer.each do |const|
225
+ @mutated_value.__send__(:remove_const, const)
226
+ end
227
+
228
+ @context.__send__(:remove_const, @const_name)
229
+ @context.const_set(@const_name, @original_value)
230
+ end
231
+
232
+ def transfer_nested_constants
233
+ @constants_to_transfer.each do |const|
234
+ @mutated_value.const_set(const, get_const_defined_on(original_value, const))
235
+ end
236
+ end
237
+
238
+ def verify_constants_to_transfer!
239
+ return [] unless should_transfer_nested_constants?
240
+
241
+ { @original_value => "the original value", @mutated_value => "the stubbed value" }.each do |value, description|
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."
248
+ end
249
+
250
+ if Array === @transfer_nested_constants
251
+ @transfer_nested_constants = @transfer_nested_constants.map(&:to_s) if RUBY_VERSION == '1.8.7'
252
+ undefined_constants = @transfer_nested_constants - constants_defined_on(@original_value)
253
+
254
+ if undefined_constants.any?
255
+ available_constants = constants_defined_on(@original_value) - @transfer_nested_constants
256
+ raise ArgumentError,
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 ')}?"
260
+ end
261
+
262
+ @transfer_nested_constants
263
+ else
264
+ constants_defined_on(@original_value)
265
+ end
266
+ end
267
+
268
+ def should_transfer_nested_constants?
269
+ return true if @transfer_nested_constants
270
+ return false unless RSpec::Mocks.configuration.transfer_nested_constants?
271
+ @original_value.respond_to?(:constants) && @mutated_value.respond_to?(:constants)
272
+ end
273
+ end
274
+
275
+ # Sets an undefined constant for the duration of an example.
276
+ #
277
+ # @private
278
+ class UndefinedConstantSetter < BaseMutator
279
+ def mutate
280
+ @parent = @context_parts.inject(Object) do |klass, name|
281
+ if const_defined_on?(klass, name)
282
+ get_const_defined_on(klass, name)
283
+ else
284
+ ConstantMutator.stub(name_for(klass, name), Module.new)
285
+ end
286
+ end
287
+
288
+ @parent.const_set(@const_name, @mutated_value)
289
+ end
290
+
291
+ def to_constant
292
+ const = super
293
+ const.stubbed = true
294
+ const.previously_defined = false
295
+
296
+ const
297
+ end
298
+
299
+ def reset
300
+ @parent.__send__(:remove_const, @const_name)
301
+ end
302
+
303
+ private
304
+
305
+ def name_for(parent, name)
306
+ root = if parent == Object
307
+ ''
308
+ else
309
+ parent.name
310
+ end
311
+ root + '::' + name
312
+ end
313
+ end
314
+
315
+ # Uses the mutator to mutate (stub or hide) a constant. Ensures that
316
+ # the mutator is correctly registered so it can be backed out at the end
317
+ # of the test.
318
+ #
319
+ # @private
320
+ def self.mutate(mutator)
321
+ ::RSpec::Mocks.space.register_constant_mutator(mutator)
322
+ mutator.mutate
323
+ end
324
+
325
+ # Used internally by the constant stubbing to raise a helpful
326
+ # error when a constant like "A::B::C" is stubbed and A::B is
327
+ # not a module (and thus, it's impossible to define "A::B::C"
328
+ # since only modules can have nested constants).
329
+ #
330
+ # @api private
331
+ def self.raise_on_invalid_const
332
+ lambda do |const_name, failed_name|
333
+ raise "Cannot stub constant #{failed_name} on #{const_name} " \
334
+ "since #{const_name} is not a module."
335
+ end
336
+ end
337
+ end
338
+ end
339
+ end
@@ -0,0 +1,149 @@
1
+ module RSpec
2
+ module Mocks
3
+ # @private
4
+ class ObjectReference
5
+ # Returns an appropriate Object or Module reference based
6
+ # on the given argument.
7
+ def self.for(object_module_or_name, allow_direct_object_refs=false)
8
+ case 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)
23
+ else
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) == ""
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)
48
+ end
49
+
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}.
54
+ # Represents a reference to that object.
55
+ # @see NamedObjectReference
56
+ class DirectObjectReference
57
+ # @param object [Object] the object to which this refers
58
+ def initialize(object)
59
+ @object = object
60
+ end
61
+
62
+ # @return [String] the object's description (via `#inspect`).
63
+ def description
64
+ @object.inspect
65
+ end
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`.
70
+ def const_to_replace
71
+ raise ArgumentError,
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
80
+ end
81
+
82
+ # Always returns true for an object as the class is defined.
83
+ #
84
+ # @return [true]
85
+ def defined?
86
+ true
87
+ end
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.
97
+ def when_loaded
98
+ yield @object
99
+ end
100
+ end
101
+
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}.
106
+ # Represents a reference to the object named (via a constant lookup)
107
+ # by the string.
108
+ # @see DirectObjectReference
109
+ class NamedObjectReference
110
+ # @param const_name [String] constant name
111
+ def initialize(const_name)
112
+ @const_name = const_name
113
+ end
114
+
115
+ # @return [Boolean] true if the named constant is defined, false otherwise.
116
+ def defined?
117
+ !!object
118
+ end
119
+
120
+ # @return [String] the constant name to replace with a double.
121
+ def const_to_replace
122
+ @const_name
123
+ end
124
+ alias description const_to_replace
125
+
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
138
+ yield object if object
139
+ end
140
+
141
+ private
142
+
143
+ def object
144
+ return @object if defined?(@object)
145
+ @object = Constant.original(@const_name).original_value
146
+ end
147
+ end
148
+ end
149
+ end