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,116 @@
1
+ module RSpec
2
+ module Mocks
3
+ module AnyInstance
4
+ # @private
5
+ # The `AnyInstance::Recorder` is responsible for redefining the klass's
6
+ # instance method in order to add any stubs/expectations the first time
7
+ # the method is called. It's not capable of updating a stub on an instance
8
+ # that's already been previously stubbed (either directly, or via
9
+ # `any_instance`).
10
+ #
11
+ # This proxy sits in front of the recorder and delegates both to it
12
+ # and to the `RSpec::Mocks::Proxy` for each already mocked or stubbed
13
+ # instance of the class, in order to propogates changes to the instances.
14
+ #
15
+ # Note that unlike `RSpec::Mocks::Proxy`, this proxy class is stateless
16
+ # and is not persisted in `RSpec::Mocks.space`.
17
+ #
18
+ # Proxying for the message expectation fluent interface (typically chained
19
+ # off of the return value of one of these methods) is provided by the
20
+ # `FluentInterfaceProxy` class below.
21
+ class Proxy
22
+ def initialize(recorder, target_proxies)
23
+ @recorder = recorder
24
+ @target_proxies = target_proxies
25
+ end
26
+
27
+ def klass
28
+ @recorder.klass
29
+ end
30
+
31
+ def stub(method_name_or_method_map, &block)
32
+ if Hash === method_name_or_method_map
33
+ method_name_or_method_map.each do |method_name, return_value|
34
+ stub(method_name).and_return(return_value)
35
+ end
36
+ else
37
+ perform_proxying(__method__, [method_name_or_method_map], block) do |proxy|
38
+ proxy.add_stub(method_name_or_method_map, &block)
39
+ end
40
+ end
41
+ end
42
+
43
+ def unstub(method_name)
44
+ perform_proxying(__method__, [method_name], nil) do |proxy|
45
+ proxy.remove_stub_if_present(method_name)
46
+ end
47
+ end
48
+
49
+ def stub_chain(*chain, &block)
50
+ perform_proxying(__method__, chain, block) do |proxy|
51
+ Mocks::StubChain.stub_chain_on(proxy.object, *chain, &block)
52
+ end
53
+ end
54
+
55
+ def expect_chain(*chain, &block)
56
+ perform_proxying(__method__, chain, block) do |proxy|
57
+ Mocks::ExpectChain.expect_chain_on(proxy.object, *chain, &block)
58
+ end
59
+ end
60
+
61
+ def should_receive(method_name, &block)
62
+ perform_proxying(__method__, [method_name], block) do |proxy|
63
+ # Yeah, this is a bit odd...but if we used `add_message_expectation`
64
+ # then it would act like `expect_every_instance_of(klass).to receive`.
65
+ # The any_instance recorder takes care of validating that an instance
66
+ # received the message.
67
+ proxy.add_stub(method_name, &block)
68
+ end
69
+ end
70
+
71
+ def should_not_receive(method_name, &block)
72
+ perform_proxying(__method__, [method_name], block) do |proxy|
73
+ proxy.add_message_expectation(method_name, &block).never
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def perform_proxying(method_name, args, block, &target_proxy_block)
80
+ recorder_value = @recorder.__send__(method_name, *args, &block)
81
+ proxy_values = @target_proxies.map(&target_proxy_block)
82
+ FluentInterfaceProxy.new([recorder_value] + proxy_values)
83
+ end
84
+ end
85
+
86
+ # @private
87
+ # Delegates messages to each of the given targets in order to
88
+ # provide the fluent interface that is available off of message
89
+ # expectations when dealing with `any_instance`.
90
+ #
91
+ # `targets` will typically contain 1 of the `AnyInstance::Recorder`
92
+ # return values and N `MessageExpectation` instances (one per instance
93
+ # of the `any_instance` klass).
94
+ class FluentInterfaceProxy
95
+ def initialize(targets)
96
+ @targets = targets
97
+ end
98
+
99
+ if RUBY_VERSION.to_f > 1.8
100
+ def respond_to_missing?(method_name, include_private=false)
101
+ super || @targets.first.respond_to?(method_name, include_private)
102
+ end
103
+ else
104
+ def respond_to?(method_name, include_private=false)
105
+ super || @targets.first.respond_to?(method_name, include_private)
106
+ end
107
+ end
108
+
109
+ def method_missing(*args, &block)
110
+ return_values = @targets.map { |t| t.__send__(*args, &block) }
111
+ FluentInterfaceProxy.new(return_values)
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,289 @@
1
+ module RSpec
2
+ module Mocks
3
+ module AnyInstance
4
+ # Given a class `TheClass`, `TheClass.any_instance` returns a `Recorder`,
5
+ # which records stubs and message expectations for later playback on
6
+ # instances of `TheClass`.
7
+ #
8
+ # Further constraints are stored in instances of [Chain](Chain).
9
+ #
10
+ # @see AnyInstance
11
+ # @see Chain
12
+ class Recorder
13
+ # @private
14
+ attr_reader :message_chains, :stubs, :klass
15
+
16
+ def initialize(klass)
17
+ @message_chains = MessageChains.new
18
+ @stubs = Hash.new { |hash, key| hash[key] = [] }
19
+ @observed_methods = []
20
+ @played_methods = {}
21
+ @backed_up_method_owner = {}
22
+ @klass = klass
23
+ @expectation_set = false
24
+ end
25
+
26
+ # Initializes the recording a stub to be played back against any
27
+ # instance of this object that invokes the submitted method.
28
+ #
29
+ # @see Methods#stub
30
+ def stub(method_name, &block)
31
+ observe!(method_name)
32
+ message_chains.add(method_name, StubChain.new(self, method_name, &block))
33
+ end
34
+
35
+ # Initializes the recording a stub chain to be played back against any
36
+ # instance of this object that invokes the method matching the first
37
+ # argument.
38
+ #
39
+ # @see Methods#stub_chain
40
+ def stub_chain(*method_names_and_optional_return_values, &block)
41
+ normalize_chain(*method_names_and_optional_return_values) do |method_name, args|
42
+ observe!(method_name)
43
+ message_chains.add(method_name, StubChainChain.new(self, *args, &block))
44
+ end
45
+ end
46
+
47
+ # @private
48
+ def expect_chain(*method_names_and_optional_return_values, &block)
49
+ @expectation_set = true
50
+ normalize_chain(*method_names_and_optional_return_values) do |method_name, args|
51
+ observe!(method_name)
52
+ message_chains.add(method_name, ExpectChainChain.new(self, *args, &block))
53
+ end
54
+ end
55
+
56
+ # Initializes the recording a message expectation to be played back
57
+ # against any instance of this object that invokes the submitted
58
+ # method.
59
+ #
60
+ # @see Methods#should_receive
61
+ def should_receive(method_name, &block)
62
+ @expectation_set = true
63
+ observe!(method_name)
64
+ message_chains.add(method_name, PositiveExpectationChain.new(self, method_name, &block))
65
+ end
66
+
67
+ # The opposite of `should_receive`
68
+ #
69
+ # @see Methods#should_not_receive
70
+ def should_not_receive(method_name, &block)
71
+ should_receive(method_name, &block).never
72
+ end
73
+
74
+ # Removes any previously recorded stubs, stub_chains or message
75
+ # expectations that use `method_name`.
76
+ #
77
+ # @see Methods#unstub
78
+ def unstub(method_name)
79
+ unless @observed_methods.include?(method_name.to_sym)
80
+ AnyInstance.error_generator.raise_method_not_stubbed_error(method_name)
81
+ end
82
+ message_chains.remove_stub_chains_for!(method_name)
83
+ stubs[method_name].clear
84
+ stop_observing!(method_name) unless message_chains.has_expectation?(method_name)
85
+ end
86
+
87
+ # @api private
88
+ #
89
+ # Used internally to verify that message expectations have been
90
+ # fulfilled.
91
+ def verify
92
+ return unless @expectation_set
93
+ return if message_chains.all_expectations_fulfilled?
94
+
95
+ AnyInstance.error_generator.raise_second_instance_received_message_error(message_chains.unfulfilled_expectations)
96
+ end
97
+
98
+ # @private
99
+ def stop_all_observation!
100
+ @observed_methods.each { |method_name| restore_method!(method_name) }
101
+ end
102
+
103
+ # @private
104
+ def playback!(instance, method_name)
105
+ RSpec::Mocks.space.ensure_registered(instance)
106
+ message_chains.playback!(instance, method_name)
107
+ @played_methods[method_name] = instance
108
+ received_expected_message!(method_name) if message_chains.has_expectation?(method_name)
109
+ end
110
+
111
+ # @private
112
+ def instance_that_received(method_name)
113
+ @played_methods[method_name]
114
+ end
115
+
116
+ # @private
117
+ def build_alias_method_name(method_name)
118
+ "__#{method_name}_without_any_instance__"
119
+ end
120
+
121
+ # @private
122
+ def already_observing?(method_name)
123
+ @observed_methods.include?(method_name) || super_class_observing?(method_name)
124
+ end
125
+
126
+ # @private
127
+ def notify_received_message(_object, message, args, _blk)
128
+ has_expectation = false
129
+
130
+ message_chains.each_unfulfilled_expectation_matching(message, *args) do |expectation|
131
+ has_expectation = true
132
+ expectation.expectation_fulfilled!
133
+ end
134
+
135
+ return unless has_expectation
136
+
137
+ restore_method!(message)
138
+ mark_invoked!(message)
139
+ end
140
+
141
+ protected
142
+
143
+ def stop_observing!(method_name)
144
+ restore_method!(method_name)
145
+ @observed_methods.delete(method_name)
146
+ super_class_observers_for(method_name).each do |ancestor|
147
+ ::RSpec::Mocks.space.
148
+ any_instance_recorder_for(ancestor).stop_observing!(method_name)
149
+ end
150
+ end
151
+
152
+ private
153
+
154
+ def ancestor_is_an_observer?(method_name)
155
+ lambda do |ancestor|
156
+ unless ancestor == @klass
157
+ ::RSpec::Mocks.space.
158
+ any_instance_recorder_for(ancestor).already_observing?(method_name)
159
+ end
160
+ end
161
+ end
162
+
163
+ def super_class_observers_for(method_name)
164
+ @klass.ancestors.select(&ancestor_is_an_observer?(method_name))
165
+ end
166
+
167
+ def super_class_observing?(method_name)
168
+ @klass.ancestors.any?(&ancestor_is_an_observer?(method_name))
169
+ end
170
+
171
+ def normalize_chain(*args)
172
+ args.shift.to_s.split('.').map { |s| s.to_sym }.reverse.each { |a| args.unshift a }
173
+ yield args.first, args
174
+ end
175
+
176
+ def received_expected_message!(method_name)
177
+ message_chains.received_expected_message!(method_name)
178
+ restore_method!(method_name)
179
+ mark_invoked!(method_name)
180
+ end
181
+
182
+ def restore_method!(method_name)
183
+ if public_protected_or_private_method_defined?(build_alias_method_name(method_name))
184
+ restore_original_method!(method_name)
185
+ else
186
+ remove_dummy_method!(method_name)
187
+ end
188
+ end
189
+
190
+ def restore_original_method!(method_name)
191
+ return unless @klass.instance_method(method_name).owner == @klass
192
+
193
+ alias_method_name = build_alias_method_name(method_name)
194
+ @klass.class_exec(@backed_up_method_owner) do |backed_up_method_owner|
195
+ remove_method method_name
196
+
197
+ # A @klass can have methods implemented (see Method#owner) in @klass
198
+ # or inherited from a superclass. In ruby 2.2 and earlier, we can copy
199
+ # a method regardless of the 'owner' and restore it to @klass after
200
+ # because a call to 'super' from @klass's copied method would end up
201
+ # calling the original class's superclass's method.
202
+ #
203
+ # With the commit below, available starting in 2.3.0, ruby changed
204
+ # this behavior and a call to 'super' from the method copied to @klass
205
+ # will call @klass's superclass method, which is the original
206
+ # implementer of this method! This leads to very strange errors
207
+ # if @klass's copied method calls 'super', since it would end up
208
+ # calling itself, the original method implemented in @klass's
209
+ # superclass.
210
+ #
211
+ # For ruby 2.3 and above, we need to only restore methods that
212
+ # @klass originally owned.
213
+ #
214
+ # https://github.com/ruby/ruby/commit/c8854d2ca4be9ee6946e6d17b0e17d9ef130ee81
215
+ if RUBY_VERSION < "2.3" || backed_up_method_owner[method_name.to_sym] == self
216
+ alias_method method_name, alias_method_name
217
+ end
218
+ remove_method alias_method_name
219
+ end
220
+ end
221
+
222
+ def remove_dummy_method!(method_name)
223
+ @klass.class_exec do
224
+ remove_method method_name
225
+ end
226
+ end
227
+
228
+ def backup_method!(method_name)
229
+ return unless public_protected_or_private_method_defined?(method_name)
230
+
231
+ alias_method_name = build_alias_method_name(method_name)
232
+ @backed_up_method_owner[method_name.to_sym] ||= @klass.instance_method(method_name).owner
233
+ @klass.class_exec do
234
+ alias_method alias_method_name, method_name
235
+ end
236
+ end
237
+
238
+ def public_protected_or_private_method_defined?(method_name)
239
+ MethodReference.method_defined_at_any_visibility?(@klass, method_name)
240
+ end
241
+
242
+ def observe!(method_name)
243
+ allow_no_prepended_module_definition_of(method_name)
244
+
245
+ if RSpec::Mocks.configuration.verify_partial_doubles? && !Mocks.configuration.temporarily_suppress_partial_double_verification
246
+ unless public_protected_or_private_method_defined?(method_name)
247
+ AnyInstance.error_generator.raise_does_not_implement_error(@klass, method_name)
248
+ end
249
+ end
250
+
251
+ stop_observing!(method_name) if already_observing?(method_name)
252
+ @observed_methods << method_name
253
+ backup_method!(method_name)
254
+ recorder = self
255
+ @klass.__send__(:define_method, method_name) do |*args, &blk|
256
+ recorder.playback!(self, method_name)
257
+ __send__(method_name, *args, &blk)
258
+ end
259
+ end
260
+
261
+ def mark_invoked!(method_name)
262
+ backup_method!(method_name)
263
+ recorder = self
264
+ @klass.__send__(:define_method, method_name) do |*_args, &_blk|
265
+ invoked_instance = recorder.instance_that_received(method_name)
266
+ inspect = "#<#{self.class}:#{object_id} #{instance_variables.map { |name| "#{name}=#{instance_variable_get name}" }.join(', ')}>"
267
+ AnyInstance.error_generator.raise_message_already_received_by_other_instance_error(
268
+ method_name, inspect, invoked_instance
269
+ )
270
+ end
271
+ end
272
+
273
+ if Support::RubyFeatures.module_prepends_supported?
274
+ def allow_no_prepended_module_definition_of(method_name)
275
+ prepended_modules = RSpec::Mocks::Proxy.prepended_modules_of(@klass)
276
+ problem_mod = prepended_modules.find { |mod| mod.method_defined?(method_name) }
277
+ return unless problem_mod
278
+
279
+ AnyInstance.error_generator.raise_not_supported_with_prepend_error(method_name, problem_mod)
280
+ end
281
+ else
282
+ def allow_no_prepended_module_definition_of(_method_name)
283
+ # nothing to do; prepends aren't supported on this version of ruby
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end
@@ -0,0 +1,51 @@
1
+ module RSpec
2
+ module Mocks
3
+ module AnyInstance
4
+ # @private
5
+ class StubChain < Chain
6
+ # @private
7
+ def expectation_fulfilled?
8
+ true
9
+ end
10
+
11
+ private
12
+
13
+ def create_message_expectation_on(instance)
14
+ proxy = ::RSpec::Mocks.space.proxy_for(instance)
15
+ method_name, opts = @expectation_args
16
+ opts = (opts || {}).merge(:expected_form => IGNORED_BACKTRACE_LINE)
17
+
18
+ stub = proxy.add_stub(method_name, opts, &@expectation_block)
19
+ @recorder.stubs[stub.message] << stub
20
+
21
+ if RSpec::Mocks.configuration.yield_receiver_to_any_instance_implementation_blocks?
22
+ stub.and_yield_receiver_to_implementation
23
+ end
24
+
25
+ stub
26
+ end
27
+
28
+ InvocationOrder =
29
+ {
30
+ :and_return => [:with, nil],
31
+ :and_raise => [:with, nil],
32
+ :and_yield => [:with, :and_yield, nil],
33
+ :and_throw => [:with, nil],
34
+ :and_call_original => [:with, nil],
35
+ :and_wrap_original => [:with, nil]
36
+ }.freeze
37
+
38
+ EmptyInvocationOrder = {}.freeze
39
+
40
+ def invocation_order
41
+ InvocationOrder
42
+ end
43
+
44
+ def verify_invocation_order(rspec_method_name, *_args, &_block)
45
+ return if invocation_order.fetch(rspec_method_name, [nil]).include?(last_message)
46
+ raise NoMethodError, "Undefined method #{rspec_method_name}"
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end