rspec-mocks 2.11.3 → 3.11.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +1 -1
  5. data/Changelog.md +989 -21
  6. data/{License.txt → LICENSE.md} +5 -3
  7. data/README.md +260 -73
  8. data/lib/rspec/mocks/any_instance/chain.rb +58 -24
  9. data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
  10. data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +31 -0
  11. data/lib/rspec/mocks/any_instance/expectation_chain.rb +23 -30
  12. data/lib/rspec/mocks/any_instance/message_chains.rb +38 -15
  13. data/lib/rspec/mocks/any_instance/proxy.rb +116 -0
  14. data/lib/rspec/mocks/any_instance/recorder.rb +155 -59
  15. data/lib/rspec/mocks/any_instance/stub_chain.rb +33 -19
  16. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +10 -12
  17. data/lib/rspec/mocks/any_instance.rb +11 -81
  18. data/lib/rspec/mocks/argument_list_matcher.rb +59 -37
  19. data/lib/rspec/mocks/argument_matchers.rb +233 -149
  20. data/lib/rspec/mocks/configuration.rb +212 -0
  21. data/lib/rspec/mocks/error_generator.rb +304 -49
  22. data/lib/rspec/mocks/example_methods.rb +361 -22
  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 +133 -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 +648 -314
  32. data/lib/rspec/mocks/method_double.rb +185 -58
  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 +48 -7
  38. data/lib/rspec/mocks/proxy.rb +405 -74
  39. data/lib/rspec/mocks/space.rb +219 -15
  40. data/lib/rspec/mocks/standalone.rb +2 -2
  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 +125 -57
  44. data/lib/rspec/mocks/verifying_double.rb +121 -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 +3 -1
  48. data/lib/rspec/mocks.rb +121 -27
  49. data.tar.gz.sig +0 -0
  50. metadata +178 -253
  51. metadata.gz.sig +3 -0
  52. data/features/README.md +0 -67
  53. data/features/Scope.md +0 -17
  54. data/features/Upgrade.md +0 -22
  55. data/features/argument_matchers/README.md +0 -27
  56. data/features/argument_matchers/explicit.feature +0 -60
  57. data/features/argument_matchers/general_matchers.feature +0 -85
  58. data/features/argument_matchers/type_matchers.feature +0 -27
  59. data/features/message_expectations/README.md +0 -69
  60. data/features/message_expectations/any_instance.feature +0 -21
  61. data/features/message_expectations/block_local_expectations.feature.pending +0 -55
  62. data/features/message_expectations/expect_message.feature +0 -94
  63. data/features/message_expectations/receive_counts.feature +0 -209
  64. data/features/message_expectations/warn_when_expectation_is_set_on_nil.feature +0 -50
  65. data/features/method_stubs/README.md +0 -47
  66. data/features/method_stubs/any_instance.feature +0 -133
  67. data/features/method_stubs/as_null_object.feature +0 -35
  68. data/features/method_stubs/simple_return_value.feature +0 -64
  69. data/features/method_stubs/stub_chain.feature +0 -51
  70. data/features/method_stubs/stub_implementation.feature +0 -26
  71. data/features/method_stubs/to_ary.feature +0 -47
  72. data/features/outside_rspec/configuration.feature +0 -82
  73. data/features/outside_rspec/standalone.feature +0 -32
  74. data/features/step_definitions/additional_cli_steps.rb +0 -4
  75. data/features/stubbing_constants/README.md +0 -62
  76. data/features/stubbing_constants/stub_defined_constant.feature +0 -79
  77. data/features/stubbing_constants/stub_undefined_constant.feature +0 -50
  78. data/features/support/env.rb +0 -6
  79. data/lib/rspec/mocks/errors.rb +0 -12
  80. data/lib/rspec/mocks/extensions/instance_exec.rb +0 -34
  81. data/lib/rspec/mocks/extensions/marshal.rb +0 -23
  82. data/lib/rspec/mocks/extensions/psych.rb +0 -23
  83. data/lib/rspec/mocks/framework.rb +0 -21
  84. data/lib/rspec/mocks/methods.rb +0 -155
  85. data/lib/rspec/mocks/mock.rb +0 -7
  86. data/lib/rspec/mocks/serialization.rb +0 -34
  87. data/lib/rspec/mocks/stashed_instance_method.rb +0 -60
  88. data/lib/rspec/mocks/stub_const.rb +0 -332
  89. data/lib/spec/mocks.rb +0 -2
  90. data/spec/rspec/mocks/and_yield_spec.rb +0 -114
  91. data/spec/rspec/mocks/any_instance/message_chains_spec.rb +0 -40
  92. data/spec/rspec/mocks/any_instance_spec.rb +0 -877
  93. data/spec/rspec/mocks/any_number_of_times_spec.rb +0 -30
  94. data/spec/rspec/mocks/argument_expectation_spec.rb +0 -34
  95. data/spec/rspec/mocks/at_least_spec.rb +0 -142
  96. data/spec/rspec/mocks/at_most_spec.rb +0 -90
  97. data/spec/rspec/mocks/block_return_value_spec.rb +0 -53
  98. data/spec/rspec/mocks/bug_report_10260_spec.rb +0 -8
  99. data/spec/rspec/mocks/bug_report_10263_spec.rb +0 -25
  100. data/spec/rspec/mocks/bug_report_11545_spec.rb +0 -32
  101. data/spec/rspec/mocks/bug_report_496_spec.rb +0 -17
  102. data/spec/rspec/mocks/bug_report_600_spec.rb +0 -22
  103. data/spec/rspec/mocks/bug_report_7611_spec.rb +0 -16
  104. data/spec/rspec/mocks/bug_report_8165_spec.rb +0 -31
  105. data/spec/rspec/mocks/bug_report_830_spec.rb +0 -21
  106. data/spec/rspec/mocks/bug_report_957_spec.rb +0 -22
  107. data/spec/rspec/mocks/double_spec.rb +0 -12
  108. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +0 -95
  109. data/spec/rspec/mocks/hash_excluding_matcher_spec.rb +0 -67
  110. data/spec/rspec/mocks/hash_including_matcher_spec.rb +0 -90
  111. data/spec/rspec/mocks/mock_ordering_spec.rb +0 -103
  112. data/spec/rspec/mocks/mock_space_spec.rb +0 -58
  113. data/spec/rspec/mocks/mock_spec.rb +0 -730
  114. data/spec/rspec/mocks/multiple_return_value_spec.rb +0 -119
  115. data/spec/rspec/mocks/nil_expectation_warning_spec.rb +0 -62
  116. data/spec/rspec/mocks/null_object_mock_spec.rb +0 -106
  117. data/spec/rspec/mocks/once_counts_spec.rb +0 -52
  118. data/spec/rspec/mocks/options_hash_spec.rb +0 -35
  119. data/spec/rspec/mocks/partial_mock_spec.rb +0 -171
  120. data/spec/rspec/mocks/partial_mock_using_mocks_directly_spec.rb +0 -95
  121. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +0 -142
  122. data/spec/rspec/mocks/precise_counts_spec.rb +0 -68
  123. data/spec/rspec/mocks/record_messages_spec.rb +0 -26
  124. data/spec/rspec/mocks/serialization_spec.rb +0 -111
  125. data/spec/rspec/mocks/stash_spec.rb +0 -27
  126. data/spec/rspec/mocks/stashed_instance_method_spec.rb +0 -53
  127. data/spec/rspec/mocks/stub_chain_spec.rb +0 -154
  128. data/spec/rspec/mocks/stub_const_spec.rb +0 -334
  129. data/spec/rspec/mocks/stub_implementation_spec.rb +0 -81
  130. data/spec/rspec/mocks/stub_spec.rb +0 -247
  131. data/spec/rspec/mocks/stubbed_message_expectations_spec.rb +0 -47
  132. data/spec/rspec/mocks/test_double_spec.rb +0 -57
  133. data/spec/rspec/mocks/to_ary_spec.rb +0 -40
  134. data/spec/rspec/mocks/twice_counts_spec.rb +0 -66
  135. data/spec/rspec/mocks_spec.rb +0 -51
  136. data/spec/spec_helper.rb +0 -21
@@ -1,33 +1,237 @@
1
+ RSpec::Support.require_rspec_support 'reentrant_mutex'
2
+
1
3
  module RSpec
2
4
  module Mocks
3
- # @api private
5
+ # @private
6
+ # Provides a default space implementation for outside
7
+ # the scope of an example. Called "root" because it serves
8
+ # as the root of the space stack.
9
+ class RootSpace
10
+ def proxy_for(*_args)
11
+ raise_lifecycle_message
12
+ end
13
+
14
+ def any_instance_recorder_for(*_args)
15
+ raise_lifecycle_message
16
+ end
17
+
18
+ def any_instance_proxy_for(*_args)
19
+ raise_lifecycle_message
20
+ end
21
+
22
+ def register_constant_mutator(_mutator)
23
+ raise_lifecycle_message
24
+ end
25
+
26
+ def any_instance_recorders_from_ancestry_of(_object)
27
+ raise_lifecycle_message
28
+ end
29
+
30
+ def reset_all
31
+ end
32
+
33
+ def verify_all
34
+ end
35
+
36
+ def registered?(_object)
37
+ false
38
+ end
39
+
40
+ def superclass_proxy_for(*_args)
41
+ raise_lifecycle_message
42
+ end
43
+
44
+ def new_scope
45
+ Space.new
46
+ end
47
+
48
+ private
49
+
50
+ def raise_lifecycle_message
51
+ raise OutsideOfExampleError,
52
+ "The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported."
53
+ end
54
+ end
55
+
56
+ # @private
4
57
  class Space
5
- def add(obj)
6
- receivers << obj unless receivers.detect {|m| m.equal? obj}
58
+ attr_reader :proxies, :any_instance_recorders, :proxy_mutex, :any_instance_mutex
59
+
60
+ def initialize
61
+ @proxies = {}
62
+ @any_instance_recorders = {}
63
+ @constant_mutators = []
64
+ @expectation_ordering = OrderGroup.new
65
+ @proxy_mutex = new_mutex
66
+ @any_instance_mutex = new_mutex
67
+ end
68
+
69
+ def new_scope
70
+ NestedSpace.new(self)
7
71
  end
8
72
 
9
73
  def verify_all
10
- receivers.each do |mock|
11
- mock.rspec_verify
12
- end
74
+ proxies.values.each { |proxy| proxy.verify }
75
+ any_instance_recorders.each_value { |recorder| recorder.verify }
13
76
  end
14
77
 
15
78
  def reset_all
16
- receivers.each do |mock|
17
- mock.rspec_reset
79
+ proxies.each_value { |proxy| proxy.reset }
80
+ @constant_mutators.reverse.each { |mut| mut.idempotently_reset }
81
+ any_instance_recorders.each_value { |recorder| recorder.stop_all_observation! }
82
+ any_instance_recorders.clear
83
+ end
84
+
85
+ def register_constant_mutator(mutator)
86
+ @constant_mutators << mutator
87
+ end
88
+
89
+ def constant_mutator_for(name)
90
+ @constant_mutators.find { |m| m.full_constant_name == name }
91
+ end
92
+
93
+ def any_instance_recorder_for(klass, only_return_existing=false)
94
+ any_instance_mutex.synchronize do
95
+ id = klass.__id__
96
+ any_instance_recorders.fetch(id) do
97
+ return nil if only_return_existing
98
+ any_instance_recorder_not_found_for(id, klass)
99
+ end
100
+ end
101
+ end
102
+
103
+ def any_instance_proxy_for(klass)
104
+ AnyInstance::Proxy.new(any_instance_recorder_for(klass), proxies_of(klass))
105
+ end
106
+
107
+ def proxies_of(klass)
108
+ proxies.values.select { |proxy| klass === proxy.object }
109
+ end
110
+
111
+ def proxy_for(object)
112
+ proxy_mutex.synchronize do
113
+ id = id_for(object)
114
+ proxies.fetch(id) { proxy_not_found_for(id, object) }
115
+ end
116
+ end
117
+
118
+ def superclass_proxy_for(klass)
119
+ proxy_mutex.synchronize do
120
+ id = id_for(klass)
121
+ proxies.fetch(id) { superclass_proxy_not_found_for(id, klass) }
122
+ end
123
+ end
124
+
125
+ alias ensure_registered proxy_for
126
+
127
+ def registered?(object)
128
+ proxies.key?(id_for object)
129
+ end
130
+
131
+ def any_instance_recorders_from_ancestry_of(object)
132
+ # Optimization: `any_instance` is a feature we generally
133
+ # recommend not using, so we can often early exit here
134
+ # without doing an O(N) linear search over the number of
135
+ # ancestors in the object's class hierarchy.
136
+ return [] if any_instance_recorders.empty?
137
+
138
+ # We access the ancestors through the singleton class, to avoid calling
139
+ # `class` in case `class` has been stubbed.
140
+ (class << object; ancestors; end).map do |klass|
141
+ any_instance_recorders[klass.__id__]
142
+ end.compact
143
+ end
144
+
145
+ private
146
+
147
+ def new_mutex
148
+ Support::ReentrantMutex.new
149
+ end
150
+
151
+ def proxy_not_found_for(id, object)
152
+ proxies[id] = case object
153
+ when NilClass then ProxyForNil.new(@expectation_ordering)
154
+ when TestDouble then object.__build_mock_proxy_unless_expired(@expectation_ordering)
155
+ when Class
156
+ class_proxy_with_callback_verification_strategy(object, CallbackInvocationStrategy.new)
157
+ else
158
+ if RSpec::Mocks.configuration.verify_partial_doubles?
159
+ VerifyingPartialDoubleProxy.new(object, @expectation_ordering)
160
+ else
161
+ PartialDoubleProxy.new(object, @expectation_ordering)
162
+ end
163
+ end
164
+ end
165
+
166
+ def superclass_proxy_not_found_for(id, object)
167
+ raise "superclass_proxy_not_found_for called with something that is not a class" unless Class === object
168
+ proxies[id] = class_proxy_with_callback_verification_strategy(object, NoCallbackInvocationStrategy.new)
169
+ end
170
+
171
+ def class_proxy_with_callback_verification_strategy(object, strategy)
172
+ if RSpec::Mocks.configuration.verify_partial_doubles?
173
+ VerifyingPartialClassDoubleProxy.new(
174
+ self,
175
+ object,
176
+ @expectation_ordering,
177
+ strategy
178
+ )
179
+ else
180
+ PartialClassDoubleProxy.new(self, object, @expectation_ordering)
18
181
  end
19
- receivers.clear
20
- expectation_ordering.clear
21
182
  end
22
183
 
23
- def expectation_ordering
24
- @expectation_ordering ||= OrderGroup.new
184
+ def any_instance_recorder_not_found_for(id, klass)
185
+ any_instance_recorders[id] = AnyInstance::Recorder.new(klass)
186
+ end
187
+
188
+ if defined?(::BasicObject) && !::BasicObject.method_defined?(:__id__) # for 1.9.2
189
+ require 'securerandom'
190
+
191
+ def id_for(object)
192
+ id = object.__id__
193
+
194
+ return id if object.equal?(::ObjectSpace._id2ref(id))
195
+ # this suggests that object.__id__ is proxying through to some wrapped object
196
+
197
+ object.instance_exec do
198
+ @__id_for_rspec_mocks_space ||= ::SecureRandom.uuid
199
+ end
200
+ end
201
+ else
202
+ def id_for(object)
203
+ object.__id__
204
+ end
205
+ end
206
+ end
207
+
208
+ # @private
209
+ class NestedSpace < Space
210
+ def initialize(parent)
211
+ @parent = parent
212
+ super()
213
+ end
214
+
215
+ def proxies_of(klass)
216
+ super + @parent.proxies_of(klass)
217
+ end
218
+
219
+ def constant_mutator_for(name)
220
+ super || @parent.constant_mutator_for(name)
221
+ end
222
+
223
+ def registered?(object)
224
+ super || @parent.registered?(object)
25
225
  end
26
226
 
27
227
  private
28
-
29
- def receivers
30
- @receivers ||= []
228
+
229
+ def proxy_not_found_for(id, object)
230
+ @parent.proxies[id] || super
231
+ end
232
+
233
+ def any_instance_recorder_not_found_for(id, klass)
234
+ @parent.any_instance_recorders[id] || super
31
235
  end
32
236
  end
33
237
  end
@@ -1,3 +1,3 @@
1
1
  require 'rspec/mocks'
2
-
3
- RSpec::Mocks.setup(self)
2
+ extend RSpec::Mocks::ExampleMethods
3
+ RSpec::Mocks.setup
@@ -0,0 +1,325 @@
1
+ module RSpec
2
+ module Mocks
3
+ # @api private
4
+ # Provides methods for enabling and disabling the available syntaxes
5
+ # provided by rspec-mocks.
6
+ module Syntax
7
+ # @private
8
+ def self.warn_about_should!
9
+ @warn_about_should = true
10
+ end
11
+
12
+ # @private
13
+ def self.warn_unless_should_configured(method_name , replacement="the new `:expect` syntax or explicitly enable `:should`")
14
+ if @warn_about_should
15
+ RSpec.deprecate(
16
+ "Using `#{method_name}` from rspec-mocks' old `:should` syntax without explicitly enabling the syntax",
17
+ :replacement => replacement
18
+ )
19
+
20
+ @warn_about_should = false
21
+ end
22
+ end
23
+
24
+ # @api private
25
+ # Enables the should syntax (`dbl.stub`, `dbl.should_receive`, etc).
26
+ def self.enable_should(syntax_host=default_should_syntax_host)
27
+ @warn_about_should = false if syntax_host == default_should_syntax_host
28
+ return if should_enabled?(syntax_host)
29
+
30
+ syntax_host.class_exec do
31
+ def should_receive(message, opts={}, &block)
32
+ ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
33
+ ::RSpec::Mocks.expect_message(self, message, opts, &block)
34
+ end
35
+
36
+ def should_not_receive(message, &block)
37
+ ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
38
+ ::RSpec::Mocks.expect_message(self, message, {}, &block).never
39
+ end
40
+
41
+ def stub(message_or_hash, opts={}, &block)
42
+ ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
43
+ if ::Hash === message_or_hash
44
+ message_or_hash.each { |message, value| stub(message).and_return value }
45
+ else
46
+ ::RSpec::Mocks.allow_message(self, message_or_hash, opts, &block)
47
+ end
48
+ end
49
+
50
+ def unstub(message)
51
+ ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__, "`allow(...).to receive(...).and_call_original` or explicitly enable `:should`")
52
+ ::RSpec::Mocks.space.proxy_for(self).remove_stub(message)
53
+ end
54
+
55
+ def stub_chain(*chain, &blk)
56
+ ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
57
+ ::RSpec::Mocks::StubChain.stub_chain_on(self, *chain, &blk)
58
+ end
59
+
60
+ def as_null_object
61
+ ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
62
+ @_null_object = true
63
+ ::RSpec::Mocks.space.proxy_for(self).as_null_object
64
+ end
65
+
66
+ def null_object?
67
+ ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
68
+ defined?(@_null_object)
69
+ end
70
+
71
+ def received_message?(message, *args, &block)
72
+ ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
73
+ ::RSpec::Mocks.space.proxy_for(self).received_message?(message, *args, &block)
74
+ end
75
+
76
+ unless Class.respond_to? :any_instance
77
+ Class.class_exec do
78
+ def any_instance
79
+ ::RSpec::Mocks::Syntax.warn_unless_should_configured(__method__)
80
+ ::RSpec::Mocks.space.any_instance_proxy_for(self)
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ # @api private
88
+ # Disables the should syntax (`dbl.stub`, `dbl.should_receive`, etc).
89
+ def self.disable_should(syntax_host=default_should_syntax_host)
90
+ return unless should_enabled?(syntax_host)
91
+
92
+ syntax_host.class_exec do
93
+ undef should_receive
94
+ undef should_not_receive
95
+ undef stub
96
+ undef unstub
97
+ undef stub_chain
98
+ undef as_null_object
99
+ undef null_object?
100
+ undef received_message?
101
+ end
102
+
103
+ Class.class_exec do
104
+ undef any_instance
105
+ end
106
+ end
107
+
108
+ # @api private
109
+ # Enables the expect syntax (`expect(dbl).to receive`, `allow(dbl).to receive`, etc).
110
+ def self.enable_expect(syntax_host=::RSpec::Mocks::ExampleMethods)
111
+ return if expect_enabled?(syntax_host)
112
+
113
+ syntax_host.class_exec do
114
+ def receive(method_name, &block)
115
+ Matchers::Receive.new(method_name, block)
116
+ end
117
+
118
+ def receive_messages(message_return_value_hash)
119
+ matcher = Matchers::ReceiveMessages.new(message_return_value_hash)
120
+ matcher.warn_about_block if block_given?
121
+ matcher
122
+ end
123
+
124
+ def receive_message_chain(*messages, &block)
125
+ Matchers::ReceiveMessageChain.new(messages, &block)
126
+ end
127
+
128
+ def allow(target)
129
+ AllowanceTarget.new(target)
130
+ end
131
+
132
+ def expect_any_instance_of(klass)
133
+ AnyInstanceExpectationTarget.new(klass)
134
+ end
135
+
136
+ def allow_any_instance_of(klass)
137
+ AnyInstanceAllowanceTarget.new(klass)
138
+ end
139
+ end
140
+
141
+ RSpec::Mocks::ExampleMethods::ExpectHost.class_exec do
142
+ def expect(target)
143
+ ExpectationTarget.new(target)
144
+ end
145
+ end
146
+ end
147
+
148
+ # @api private
149
+ # Disables the expect syntax (`expect(dbl).to receive`, `allow(dbl).to receive`, etc).
150
+ def self.disable_expect(syntax_host=::RSpec::Mocks::ExampleMethods)
151
+ return unless expect_enabled?(syntax_host)
152
+
153
+ syntax_host.class_exec do
154
+ undef receive
155
+ undef receive_messages
156
+ undef receive_message_chain
157
+ undef allow
158
+ undef expect_any_instance_of
159
+ undef allow_any_instance_of
160
+ end
161
+
162
+ RSpec::Mocks::ExampleMethods::ExpectHost.class_exec do
163
+ undef expect
164
+ end
165
+ end
166
+
167
+ # @api private
168
+ # Indicates whether or not the should syntax is enabled.
169
+ def self.should_enabled?(syntax_host=default_should_syntax_host)
170
+ syntax_host.method_defined?(:should_receive)
171
+ end
172
+
173
+ # @api private
174
+ # Indicates whether or not the expect syntax is enabled.
175
+ def self.expect_enabled?(syntax_host=::RSpec::Mocks::ExampleMethods)
176
+ syntax_host.method_defined?(:allow)
177
+ end
178
+
179
+ # @api private
180
+ # Determines where the methods like `should_receive`, and `stub` are added.
181
+ def self.default_should_syntax_host
182
+ # JRuby 1.7.4 introduces a regression whereby `defined?(::BasicObject) => nil`
183
+ # yet `BasicObject` still exists and patching onto ::Object breaks things
184
+ # e.g. SimpleDelegator expectations won't work
185
+ #
186
+ # See: https://github.com/jruby/jruby/issues/814
187
+ if defined?(JRUBY_VERSION) && JRUBY_VERSION == '1.7.4' && RUBY_VERSION.to_f > 1.8
188
+ return ::BasicObject
189
+ end
190
+
191
+ # On 1.8.7, Object.ancestors.last == Kernel but
192
+ # things blow up if we include `RSpec::Mocks::Methods`
193
+ # into Kernel...not sure why.
194
+ return Object unless defined?(::BasicObject)
195
+
196
+ # MacRuby has BasicObject but it's not the root class.
197
+ return Object unless Object.ancestors.last == ::BasicObject
198
+
199
+ ::BasicObject
200
+ end
201
+ end
202
+ end
203
+ end
204
+
205
+ if defined?(BasicObject)
206
+ # The legacy `:should` syntax adds the following methods directly to
207
+ # `BasicObject` so that they are available off of any object. Note, however,
208
+ # that this syntax does not always play nice with delegate/proxy objects.
209
+ # We recommend you use the non-monkeypatching `:expect` syntax instead.
210
+ # @see Class
211
+ class BasicObject
212
+ # @method should_receive
213
+ # Sets an expectation that this object should receive a message before
214
+ # the end of the example.
215
+ #
216
+ # @example
217
+ # logger = double('logger')
218
+ # thing_that_logs = ThingThatLogs.new(logger)
219
+ # logger.should_receive(:log)
220
+ # thing_that_logs.do_something_that_logs_a_message
221
+ #
222
+ # @note This is only available when you have enabled the `should` syntax.
223
+ # @see RSpec::Mocks::ExampleMethods#expect
224
+
225
+ # @method should_not_receive
226
+ # Sets and expectation that this object should _not_ receive a message
227
+ # during this example.
228
+ # @see RSpec::Mocks::ExampleMethods#expect
229
+
230
+ # @method stub
231
+ # Tells the object to respond to the message with the specified value.
232
+ #
233
+ # @example
234
+ # counter.stub(:count).and_return(37)
235
+ # counter.stub(:count => 37)
236
+ # counter.stub(:count) { 37 }
237
+ #
238
+ # @note This is only available when you have enabled the `should` syntax.
239
+ # @see RSpec::Mocks::ExampleMethods#allow
240
+
241
+ # @method unstub
242
+ # Removes a stub. On a double, the object will no longer respond to
243
+ # `message`. On a real object, the original method (if it exists) is
244
+ # restored.
245
+ #
246
+ # This is rarely used, but can be useful when a stub is set up during a
247
+ # shared `before` hook for the common case, but you want to replace it
248
+ # for a special case.
249
+ #
250
+ # @note This is only available when you have enabled the `should` syntax.
251
+
252
+ # @method stub_chain
253
+ # @overload stub_chain(method1, method2)
254
+ # @overload stub_chain("method1.method2")
255
+ # @overload stub_chain(method1, method_to_value_hash)
256
+ #
257
+ # Stubs a chain of methods.
258
+ #
259
+ # ## Warning:
260
+ #
261
+ # Chains can be arbitrarily long, which makes it quite painless to
262
+ # violate the Law of Demeter in violent ways, so you should consider any
263
+ # use of `stub_chain` a code smell. Even though not all code smells
264
+ # indicate real problems (think fluent interfaces), `stub_chain` still
265
+ # results in brittle examples. For example, if you write
266
+ # `foo.stub_chain(:bar, :baz => 37)` in a spec and then the
267
+ # implementation calls `foo.baz.bar`, the stub will not work.
268
+ #
269
+ # @example
270
+ # double.stub_chain("foo.bar") { :baz }
271
+ # double.stub_chain(:foo, :bar => :baz)
272
+ # double.stub_chain(:foo, :bar) { :baz }
273
+ #
274
+ # # Given any of ^^ these three forms ^^:
275
+ # double.foo.bar # => :baz
276
+ #
277
+ # # Common use in Rails/ActiveRecord:
278
+ # Article.stub_chain("recent.published") { [Article.new] }
279
+ #
280
+ # @note This is only available when you have enabled the `should` syntax.
281
+ # @see RSpec::Mocks::ExampleMethods#receive_message_chain
282
+
283
+ # @method as_null_object
284
+ # Tells the object to respond to all messages. If specific stub values
285
+ # are declared, they'll work as expected. If not, the receiver is
286
+ # returned.
287
+ #
288
+ # @note This is only available when you have enabled the `should` syntax.
289
+
290
+ # @method null_object?
291
+ # Returns true if this object has received `as_null_object`
292
+ #
293
+ # @note This is only available when you have enabled the `should` syntax.
294
+ end
295
+ end
296
+
297
+ # The legacy `:should` syntax adds the `any_instance` to `Class`.
298
+ # We generally recommend you use the newer `:expect` syntax instead,
299
+ # which allows you to stub any instance of a class using
300
+ # `allow_any_instance_of(klass)` or mock any instance using
301
+ # `expect_any_instance_of(klass)`.
302
+ # @see BasicObject
303
+ class Class
304
+ # @method any_instance
305
+ # Used to set stubs and message expectations on any instance of a given
306
+ # class. Returns a [Recorder](Recorder), which records messages like
307
+ # `stub` and `should_receive` for later playback on instances of the
308
+ # class.
309
+ #
310
+ # @example
311
+ # Car.any_instance.should_receive(:go)
312
+ # race = Race.new
313
+ # race.cars << Car.new
314
+ # race.go # assuming this delegates to all of its cars
315
+ # # this example would pass
316
+ #
317
+ # Account.any_instance.stub(:balance) { Money.new(:USD, 25) }
318
+ # Account.new.balance # => Money.new(:USD, 25))
319
+ #
320
+ # @return [Recorder]
321
+ #
322
+ # @note This is only available when you have enabled the `should` syntax.
323
+ # @see RSpec::Mocks::ExampleMethods#expect_any_instance_of
324
+ # @see RSpec::Mocks::ExampleMethods#allow_any_instance_of
325
+ end
@@ -0,0 +1,124 @@
1
+ module RSpec
2
+ module Mocks
3
+ # @private
4
+ module TargetDelegationClassMethods
5
+ def delegate_to(matcher_method)
6
+ define_method(:to) do |matcher, &block|
7
+ unless matcher_allowed?(matcher)
8
+ raise_unsupported_matcher(:to, matcher)
9
+ end
10
+ define_matcher(matcher, matcher_method, &block)
11
+ end
12
+ end
13
+
14
+ def delegate_not_to(matcher_method, options={})
15
+ method_name = options.fetch(:from)
16
+ define_method(method_name) do |matcher, &block|
17
+ case matcher
18
+ when Matchers::Receive, Matchers::HaveReceived
19
+ define_matcher(matcher, matcher_method, &block)
20
+ when Matchers::ReceiveMessages, Matchers::ReceiveMessageChain
21
+ raise_negation_unsupported(method_name, matcher)
22
+ else
23
+ raise_unsupported_matcher(method_name, matcher)
24
+ end
25
+ end
26
+ end
27
+
28
+ def disallow_negation(method_name)
29
+ define_method(method_name) do |matcher, *_args|
30
+ raise_negation_unsupported(method_name, matcher)
31
+ end
32
+ end
33
+ end
34
+
35
+ # @private
36
+ module TargetDelegationInstanceMethods
37
+ attr_reader :target
38
+
39
+ private
40
+
41
+ def matcher_allowed?(matcher)
42
+ Matchers::Matcher === matcher
43
+ end
44
+
45
+ def define_matcher(matcher, name, &block)
46
+ matcher.__send__(name, target, &block)
47
+ end
48
+
49
+ def raise_unsupported_matcher(method_name, matcher)
50
+ raise UnsupportedMatcherError,
51
+ "only the `receive`, `have_received` and `receive_messages` matchers are supported " \
52
+ "with `#{expression}(...).#{method_name}`, but you have provided: #{matcher}"
53
+ end
54
+
55
+ def raise_negation_unsupported(method_name, matcher)
56
+ raise NegationUnsupportedError,
57
+ "`#{expression}(...).#{method_name} #{matcher.name}` is not supported since it " \
58
+ "doesn't really make sense. What would it even mean?"
59
+ end
60
+ end
61
+
62
+ # @private
63
+ class TargetBase
64
+ def initialize(target)
65
+ @target = target
66
+ end
67
+
68
+ extend TargetDelegationClassMethods
69
+ include TargetDelegationInstanceMethods
70
+ end
71
+
72
+ # @private
73
+ module ExpectationTargetMethods
74
+ extend TargetDelegationClassMethods
75
+ include TargetDelegationInstanceMethods
76
+
77
+ delegate_to :setup_expectation
78
+ delegate_not_to :setup_negative_expectation, :from => :not_to
79
+ delegate_not_to :setup_negative_expectation, :from => :to_not
80
+
81
+ def expression
82
+ :expect
83
+ end
84
+ end
85
+
86
+ # @private
87
+ class ExpectationTarget < TargetBase
88
+ include ExpectationTargetMethods
89
+ end
90
+
91
+ # @private
92
+ class AllowanceTarget < TargetBase
93
+ def expression
94
+ :allow
95
+ end
96
+
97
+ delegate_to :setup_allowance
98
+ disallow_negation :not_to
99
+ disallow_negation :to_not
100
+ end
101
+
102
+ # @private
103
+ class AnyInstanceAllowanceTarget < TargetBase
104
+ def expression
105
+ :allow_any_instance_of
106
+ end
107
+
108
+ delegate_to :setup_any_instance_allowance
109
+ disallow_negation :not_to
110
+ disallow_negation :to_not
111
+ end
112
+
113
+ # @private
114
+ class AnyInstanceExpectationTarget < TargetBase
115
+ def expression
116
+ :expect_any_instance_of
117
+ end
118
+
119
+ delegate_to :setup_any_instance_expectation
120
+ delegate_not_to :setup_any_instance_negative_expectation, :from => :not_to
121
+ delegate_not_to :setup_any_instance_negative_expectation, :from => :to_not
122
+ end
123
+ end
124
+ end