rspec-mocks 2.13.1 → 2.14.0.rc1

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 (99) hide show
  1. data/Changelog.md +45 -4
  2. data/README.md +1 -1
  3. data/features/argument_matchers/README.md +2 -2
  4. data/features/argument_matchers/explicit.feature +2 -3
  5. data/features/argument_matchers/general_matchers.feature +2 -2
  6. data/features/argument_matchers/type_matchers.feature +3 -4
  7. data/features/message_expectations/README.md +2 -2
  8. data/features/message_expectations/any_instance.feature +2 -2
  9. data/features/message_expectations/block_local_expectations.feature.pending +3 -3
  10. data/features/message_expectations/expect_message_using_expect.feature +103 -0
  11. data/features/message_expectations/expect_message_using_should_receive.feature +118 -0
  12. data/features/message_expectations/receive_counts.feature +1 -1
  13. data/features/method_stubs/README.md +1 -1
  14. data/features/method_stubs/any_instance.feature +11 -11
  15. data/features/method_stubs/as_null_object.feature +1 -1
  16. data/features/method_stubs/stub_implementation.feature +2 -2
  17. data/features/outside_rspec/configuration.feature +0 -20
  18. data/features/spies/spy_partial_mock_method.feature +34 -0
  19. data/features/spies/spy_pure_mock_method.feature +76 -0
  20. data/features/spies/spy_unstubbed_method.feature +18 -0
  21. data/features/step_definitions/additional_cli_steps.rb +7 -0
  22. data/features/test_frameworks/test_unit.feature +43 -0
  23. data/lib/rspec/mocks.rb +9 -34
  24. data/lib/rspec/mocks/any_instance/chain.rb +8 -2
  25. data/lib/rspec/mocks/any_instance/expectation_chain.rb +19 -16
  26. data/lib/rspec/mocks/any_instance/recorder.rb +6 -3
  27. data/lib/rspec/mocks/any_instance/stub_chain.rb +11 -11
  28. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +8 -10
  29. data/lib/rspec/mocks/argument_list_matcher.rb +7 -3
  30. data/lib/rspec/mocks/configuration.rb +28 -1
  31. data/lib/rspec/mocks/deprecation.rb +18 -0
  32. data/lib/rspec/mocks/error_generator.rb +60 -8
  33. data/lib/rspec/mocks/errors.rb +1 -1
  34. data/lib/rspec/mocks/example_methods.rb +39 -3
  35. data/lib/rspec/mocks/extensions/marshal.rb +4 -10
  36. data/lib/rspec/mocks/framework.rb +16 -4
  37. data/lib/rspec/mocks/instance_method_stasher.rb +3 -0
  38. data/lib/rspec/mocks/matchers/have_received.rb +93 -0
  39. data/lib/rspec/mocks/matchers/receive.rb +92 -0
  40. data/lib/rspec/mocks/message_expectation.rb +66 -129
  41. data/lib/rspec/mocks/method_double.rb +50 -43
  42. data/lib/rspec/mocks/mutate_const.rb +8 -20
  43. data/lib/rspec/mocks/proxy.rb +41 -25
  44. data/lib/rspec/mocks/proxy_for_nil.rb +36 -0
  45. data/lib/rspec/mocks/space.rb +64 -11
  46. data/lib/rspec/mocks/stub_chain.rb +51 -0
  47. data/lib/rspec/mocks/syntax.rb +329 -0
  48. data/lib/rspec/mocks/targets.rb +69 -0
  49. data/lib/rspec/mocks/test_double.rb +25 -4
  50. data/lib/rspec/mocks/version.rb +1 -1
  51. data/lib/spec/mocks.rb +1 -3
  52. data/spec/rspec/mocks/and_call_original_spec.rb +8 -0
  53. data/spec/rspec/mocks/and_yield_spec.rb +6 -6
  54. data/spec/rspec/mocks/any_instance_spec.rb +43 -31
  55. data/spec/rspec/mocks/any_number_of_times_spec.rb +6 -0
  56. data/spec/rspec/mocks/argument_expectation_spec.rb +12 -14
  57. data/spec/rspec/mocks/at_least_spec.rb +46 -37
  58. data/spec/rspec/mocks/at_most_spec.rb +12 -12
  59. data/spec/rspec/mocks/block_return_value_spec.rb +18 -1
  60. data/spec/rspec/mocks/bug_report_10260_spec.rb +1 -1
  61. data/spec/rspec/mocks/bug_report_10263_spec.rb +1 -1
  62. data/spec/rspec/mocks/bug_report_11545_spec.rb +4 -4
  63. data/spec/rspec/mocks/bug_report_600_spec.rb +1 -1
  64. data/spec/rspec/mocks/bug_report_7611_spec.rb +1 -1
  65. data/spec/rspec/mocks/configuration_spec.rb +124 -0
  66. data/spec/rspec/mocks/double_spec.rb +13 -1
  67. data/spec/rspec/mocks/failing_argument_matchers_spec.rb +17 -1
  68. data/spec/rspec/mocks/hash_excluding_matcher_spec.rb +13 -13
  69. data/spec/rspec/mocks/matchers/have_received_spec.rb +266 -0
  70. data/spec/rspec/mocks/matchers/receive_spec.rb +318 -0
  71. data/spec/rspec/mocks/methods_spec.rb +27 -0
  72. data/spec/rspec/mocks/mock_ordering_spec.rb +4 -4
  73. data/spec/rspec/mocks/mock_space_spec.rb +94 -39
  74. data/spec/rspec/mocks/mock_spec.rb +65 -50
  75. data/spec/rspec/mocks/multiple_return_value_spec.rb +10 -10
  76. data/spec/rspec/mocks/mutate_const_spec.rb +21 -1
  77. data/spec/rspec/mocks/nil_expectation_warning_spec.rb +10 -4
  78. data/spec/rspec/mocks/null_object_mock_spec.rb +11 -2
  79. data/spec/rspec/mocks/once_counts_spec.rb +5 -5
  80. data/spec/rspec/mocks/options_hash_spec.rb +4 -4
  81. data/spec/rspec/mocks/partial_mock_spec.rb +20 -11
  82. data/spec/rspec/mocks/partial_mock_using_mocks_directly_spec.rb +7 -7
  83. data/spec/rspec/mocks/passing_argument_matchers_spec.rb +2 -2
  84. data/spec/rspec/mocks/precise_counts_spec.rb +6 -6
  85. data/spec/rspec/mocks/serialization_spec.rb +1 -22
  86. data/spec/rspec/mocks/stash_spec.rb +4 -12
  87. data/spec/rspec/mocks/stub_implementation_spec.rb +3 -3
  88. data/spec/rspec/mocks/stub_spec.rb +44 -20
  89. data/spec/rspec/mocks/stubbed_message_expectations_spec.rb +6 -6
  90. data/spec/rspec/mocks/twice_counts_spec.rb +6 -6
  91. data/spec/rspec/mocks_spec.rb +1 -3
  92. data/spec/spec_helper.rb +25 -1
  93. metadata +86 -81
  94. data/features/message_expectations/expect_message.feature +0 -94
  95. data/lib/rspec/mocks/any_instance.rb +0 -81
  96. data/lib/rspec/mocks/extensions/psych.rb +0 -23
  97. data/lib/rspec/mocks/methods.rb +0 -155
  98. data/lib/rspec/mocks/serialization.rb +0 -34
  99. data/spec/rspec/mocks/combining_implementation_instructions_spec.rb +0 -197
@@ -0,0 +1,51 @@
1
+ module RSpec
2
+ module Mocks
3
+ # @private
4
+ class StubChain
5
+ def self.stub_chain_on(object, *chain, &blk)
6
+ new(object, *chain, &blk).stub_chain
7
+ end
8
+
9
+ attr_reader :object, :chain, :block
10
+
11
+ def initialize(object, *chain, &blk)
12
+ @object = object
13
+ @chain, @block = format_chain(*chain, &blk)
14
+ end
15
+
16
+ def stub_chain
17
+ if chain.length > 1
18
+ if matching_stub = find_matching_stub
19
+ chain.shift
20
+ matching_stub.invoke(nil).stub_chain(*chain, &block)
21
+ else
22
+ next_in_chain = Mock.new
23
+ object.stub(chain.shift) { next_in_chain }
24
+ StubChain.stub_chain_on(next_in_chain, *chain, &block)
25
+ end
26
+ else
27
+ object.stub(chain.shift, &block)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def format_chain(*chain, &blk)
34
+ if Hash === chain.last
35
+ hash = chain.pop
36
+ hash.each do |k,v|
37
+ chain << k
38
+ blk = lambda { v }
39
+ end
40
+ end
41
+ return chain.join('.').split('.'), blk
42
+ end
43
+
44
+ def find_matching_stub
45
+ ::RSpec::Mocks.proxy_for(object).
46
+ __send__(:find_matching_method_stub, chain.first.to_sym)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,329 @@
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
+
8
+ # @api private
9
+ # Enables the should syntax (`dbl.stub`, `dbl.should_receive`, etc).
10
+ def self.enable_should(syntax_host = default_should_syntax_host)
11
+ return if should_enabled?(syntax_host)
12
+
13
+ syntax_host.class_eval do
14
+ def should_receive(message, opts={}, &block)
15
+ proxy = ::RSpec::Mocks.proxy_for(self)
16
+ proxy.add_message_expectation(opts[:expected_from] || caller(1)[0], message.to_sym, opts, &block)
17
+ end
18
+
19
+ def should_not_receive(message, &block)
20
+ proxy = ::RSpec::Mocks.proxy_for(self)
21
+ proxy.add_negative_message_expectation(caller(1)[0], message.to_sym, &block)
22
+ end
23
+
24
+ def stub(message_or_hash, opts={}, &block)
25
+ if ::Hash === message_or_hash
26
+ message_or_hash.each {|message, value| stub(message).and_return value }
27
+ else
28
+ ::RSpec::Mocks.proxy_for(self).add_stub(caller(1)[0], message_or_hash.to_sym, opts, &block)
29
+ end
30
+ end
31
+
32
+ def unstub(message)
33
+ ::RSpec::Mocks.space.proxy_for(self).remove_stub(message)
34
+ end
35
+
36
+ def stub!(message_or_hash, opts={}, &block)
37
+ ::RSpec.deprecate "stub!", :replacement => "stub"
38
+ stub(message_or_hash, opts={}, &block)
39
+ end
40
+
41
+ def unstub!(message)
42
+ ::RSpec.deprecate "unstub!", :replacement => "unstub"
43
+ unstub(message)
44
+ end
45
+
46
+ def stub_chain(*chain, &blk)
47
+ ::RSpec::Mocks::StubChain.stub_chain_on(self, *chain, &blk)
48
+ end
49
+
50
+ def as_null_object
51
+ @_null_object = true
52
+ ::RSpec::Mocks.proxy_for(self).as_null_object
53
+ end
54
+
55
+ def null_object?
56
+ defined?(@_null_object)
57
+ end
58
+
59
+ def received_message?(message, *args, &block)
60
+ ::RSpec::Mocks.proxy_for(self).received_message?(message, *args, &block)
61
+ end
62
+
63
+ unless Class.respond_to? :any_instance
64
+ Class.class_eval do
65
+ def any_instance
66
+ ::RSpec::Mocks.any_instance_recorder_for(self)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ # @api private
74
+ # Disables the should syntax (`dbl.stub`, `dbl.should_receive`, etc).
75
+ def self.disable_should(syntax_host = default_should_syntax_host)
76
+ return unless should_enabled?(syntax_host)
77
+
78
+ syntax_host.class_eval do
79
+ undef should_receive
80
+ undef should_not_receive
81
+ undef stub
82
+ undef unstub
83
+ undef stub!
84
+ undef unstub!
85
+ undef stub_chain
86
+ undef as_null_object
87
+ undef null_object?
88
+ undef received_message?
89
+ end
90
+
91
+ Class.class_eval do
92
+ undef any_instance
93
+ end
94
+ end
95
+
96
+ # @api private
97
+ # Enables the expect syntax (`expect(dbl).to receive`, `allow(dbl).to receive`, etc).
98
+ def self.enable_expect(syntax_host = ::RSpec::Mocks::ExampleMethods)
99
+ return if expect_enabled?(syntax_host)
100
+
101
+ syntax_host.class_eval do
102
+ def receive(method_name, &block)
103
+ Matchers::Receive.new(method_name, block)
104
+ end
105
+
106
+ def allow(target)
107
+ AllowanceTarget.new(target)
108
+ end
109
+
110
+ def expect_any_instance_of(klass)
111
+ AnyInstanceExpectationTarget.new(klass)
112
+ end
113
+
114
+ def allow_any_instance_of(klass)
115
+ AnyInstanceAllowanceTarget.new(klass)
116
+ end
117
+ end
118
+
119
+ RSpec::Mocks::ExampleMethods::ExpectHost.class_eval do
120
+ def expect(target)
121
+ ExpectationTarget.new(target)
122
+ end
123
+ end
124
+ end
125
+
126
+ # @api private
127
+ # Disables the expect syntax (`expect(dbl).to receive`, `allow(dbl).to receive`, etc).
128
+ def self.disable_expect(syntax_host = ::RSpec::Mocks::ExampleMethods)
129
+ return unless expect_enabled?(syntax_host)
130
+
131
+ syntax_host.class_eval do
132
+ undef receive
133
+ undef allow
134
+ undef expect_any_instance_of
135
+ undef allow_any_instance_of
136
+ end
137
+
138
+ RSpec::Mocks::ExampleMethods::ExpectHost.class_eval do
139
+ undef expect
140
+ end
141
+ end
142
+
143
+ # @api private
144
+ # Indicates whether or not the should syntax is enabled.
145
+ def self.should_enabled?(syntax_host = default_should_syntax_host)
146
+ syntax_host.method_defined?(:should_receive)
147
+ end
148
+
149
+ # @api private
150
+ # Indicates whether or not the expect syntax is enabled.
151
+ def self.expect_enabled?(syntax_host = ::RSpec::Mocks::ExampleMethods)
152
+ syntax_host.method_defined?(:allow)
153
+ end
154
+
155
+ # @api private
156
+ # Determines where the methods like `should_receive`, and `stub` are added.
157
+ def self.default_should_syntax_host
158
+ # On 1.8.7, Object.ancestors.last == Kernel but
159
+ # things blow up if we include `RSpec::Mocks::Methods`
160
+ # into Kernel...not sure why.
161
+ return Object unless defined?(::BasicObject)
162
+
163
+ # MacRuby has BasicObject but it's not the root class.
164
+ return Object unless Object.ancestors.last == ::BasicObject
165
+
166
+ ::BasicObject
167
+ end
168
+
169
+ # @method should_receive
170
+ # Sets an expectation that this object should receive a message before
171
+ # the end of the example.
172
+ #
173
+ # @example
174
+ #
175
+ # logger = double('logger')
176
+ # thing_that_logs = ThingThatLogs.new(logger)
177
+ # logger.should_receive(:log)
178
+ # thing_that_logs.do_something_that_logs_a_message
179
+ #
180
+ # @note This is only available when you have enabled the `should` syntax.
181
+
182
+ # @method should_not_receive
183
+ # Sets and expectation that this object should _not_ receive a message
184
+ # during this example.
185
+
186
+ # @method stub
187
+ # Tells the object to respond to the message with the specified value.
188
+ #
189
+ # @example
190
+ #
191
+ # counter.stub(:count).and_return(37)
192
+ # counter.stub(:count => 37)
193
+ # counter.stub(:count) { 37 }
194
+ #
195
+ # @note This is only available when you have enabled the `should` syntax.
196
+
197
+ # @method unstub
198
+ # Removes a stub. On a double, the object will no longer respond to
199
+ # `message`. On a real object, the original method (if it exists) is
200
+ # restored.
201
+ #
202
+ # This is rarely used, but can be useful when a stub is set up during a
203
+ # shared `before` hook for the common case, but you want to replace it
204
+ # for a special case.
205
+ #
206
+ # @note This is only available when you have enabled the `should` syntax.
207
+
208
+ # @method stub_chain
209
+ # @overload stub_chain(method1, method2)
210
+ # @overload stub_chain("method1.method2")
211
+ # @overload stub_chain(method1, method_to_value_hash)
212
+ #
213
+ # Stubs a chain of methods.
214
+ #
215
+ # ## Warning:
216
+ #
217
+ # Chains can be arbitrarily long, which makes it quite painless to
218
+ # violate the Law of Demeter in violent ways, so you should consider any
219
+ # use of `stub_chain` a code smell. Even though not all code smells
220
+ # indicate real problems (think fluent interfaces), `stub_chain` still
221
+ # results in brittle examples. For example, if you write
222
+ # `foo.stub_chain(:bar, :baz => 37)` in a spec and then the
223
+ # implementation calls `foo.baz.bar`, the stub will not work.
224
+ #
225
+ # @example
226
+ #
227
+ # double.stub_chain("foo.bar") { :baz }
228
+ # double.stub_chain(:foo, :bar => :baz)
229
+ # double.stub_chain(:foo, :bar) { :baz }
230
+ #
231
+ # # Given any of ^^ these three forms ^^:
232
+ # double.foo.bar # => :baz
233
+ #
234
+ # # Common use in Rails/ActiveRecord:
235
+ # Article.stub_chain("recent.published") { [Article.new] }
236
+ #
237
+ # @note This is only available when you have enabled the `should` syntax.
238
+
239
+ # @method as_null_object
240
+ # Tells the object to respond to all messages. If specific stub values
241
+ # are declared, they'll work as expected. If not, the receiver is
242
+ # returned.
243
+ #
244
+ # @note This is only available when you have enabled the `should` syntax.
245
+
246
+ # @method null_object?
247
+ # Returns true if this object has received `as_null_object`
248
+ #
249
+ # @note This is only available when you have enabled the `should` syntax.
250
+
251
+ # @method any_instance
252
+ # Used to set stubs and message expectations on any instance of a given
253
+ # class. Returns a [Recorder](Recorder), which records messages like
254
+ # `stub` and `should_receive` for later playback on instances of the
255
+ # class.
256
+ #
257
+ # @example
258
+ #
259
+ # Car.any_instance.should_receive(:go)
260
+ # race = Race.new
261
+ # race.cars << Car.new
262
+ # race.go # assuming this delegates to all of its cars
263
+ # # this example would pass
264
+ #
265
+ # Account.any_instance.stub(:balance) { Money.new(:USD, 25) }
266
+ # Account.new.balance # => Money.new(:USD, 25))
267
+ #
268
+ # @return [Recorder]
269
+ #
270
+ # @note This is only available when you have enabled the `should` syntax.
271
+
272
+ # @method expect
273
+ # Used to wrap an object in preparation for setting a mock expectation
274
+ # on it.
275
+ #
276
+ # @example
277
+ #
278
+ # expect(obj).to receive(:foo).with(5).and_return(:return_value)
279
+ #
280
+ # @note This method is usually provided by rspec-expectations, unless
281
+ # you are using rspec-mocks w/o rspec-expectations, in which case it
282
+ # is only made available if you enable the `expect` syntax.
283
+
284
+ # @method allow
285
+ # Used to wrap an object in preparation for stubbing a method
286
+ # on it.
287
+ #
288
+ # @example
289
+ #
290
+ # allow(dbl).to receive(:foo).with(5).and_return(:return_value)
291
+ #
292
+ # @note This is only available when you have enabled the `expect` syntax.
293
+
294
+ # @method expect_any_instance_of
295
+ # Used to wrap a class in preparation for setting a mock expectation
296
+ # on instances of it.
297
+ #
298
+ # @example
299
+ #
300
+ # expect_any_instance_of(MyClass).to receive(:foo)
301
+ #
302
+ # @note This is only available when you have enabled the `expect` syntax.
303
+
304
+ # @method allow_any_instance_of
305
+ # Used to wrap a class in preparation for stubbing a method
306
+ # on instances of it.
307
+ #
308
+ # @example
309
+ #
310
+ # allow_any_instance_of(MyClass).to receive(:foo)
311
+ #
312
+ # @note This is only available when you have enabled the `expect` syntax.
313
+
314
+ # @method receive
315
+ # Used to specify a message that you expect or allow an object
316
+ # to receive. The object returned by `receive` supports the same
317
+ # fluent interface that `should_receive` and `stub` have always
318
+ # supported, allowing you to constrain the arguments or number of
319
+ # times, and configure how the object should respond to the message.
320
+ #
321
+ # @example
322
+ #
323
+ # expect(obj).to receive(:hello).with("world").exactly(3).times
324
+ #
325
+ # @note This is only available when you have enabled the `expect` syntax.
326
+ end
327
+ end
328
+ end
329
+
@@ -0,0 +1,69 @@
1
+ module RSpec
2
+ module Mocks
3
+ UnsupportedMatcherError = Class.new(StandardError)
4
+ NegationUnsupportedError = Class.new(StandardError)
5
+
6
+ class TargetBase
7
+ def initialize(target)
8
+ @target = target
9
+ end
10
+
11
+ def self.delegate_to(matcher_method, options = {})
12
+ method_name = options.fetch(:from) { :to }
13
+ class_eval(<<-RUBY)
14
+ def #{method_name}(matcher, &block)
15
+ unless Matchers::Receive === matcher
16
+ raise UnsupportedMatcherError, "only the `receive` matcher is supported " +
17
+ "with `\#{expression}(...).\#{#{method_name.inspect}}`, but you have provided: \#{matcher}"
18
+ end
19
+
20
+ matcher.__send__(#{matcher_method.inspect}, @target, &block)
21
+ end
22
+ RUBY
23
+ end
24
+
25
+ def self.disallow_negation(method)
26
+ define_method method do |*args|
27
+ raise NegationUnsupportedError,
28
+ "`#{expression}(...).#{method} receive` is not supported since it " +
29
+ "doesn't really make sense. What would it even mean?"
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def expression
36
+ self.class::EXPRESSION
37
+ end
38
+ end
39
+
40
+ class AllowanceTarget < TargetBase
41
+ EXPRESSION = :allow
42
+ delegate_to :setup_allowance
43
+ disallow_negation :not_to
44
+ disallow_negation :to_not
45
+ end
46
+
47
+ class ExpectationTarget < TargetBase
48
+ EXPRESSION = :expect
49
+ delegate_to :setup_expectation
50
+ delegate_to :setup_negative_expectation, :from => :not_to
51
+ delegate_to :setup_negative_expectation, :from => :to_not
52
+ end
53
+
54
+ class AnyInstanceAllowanceTarget < TargetBase
55
+ EXPRESSION = :expect_any_instance_of
56
+ delegate_to :setup_any_instance_allowance
57
+ disallow_negation :not_to
58
+ disallow_negation :to_not
59
+ end
60
+
61
+ class AnyInstanceExpectationTarget < TargetBase
62
+ EXPRESSION = :expect_any_instance_of
63
+ delegate_to :setup_any_instance_expectation
64
+ delegate_to :setup_any_instance_negative_expectation, :from => :not_to
65
+ delegate_to :setup_any_instance_negative_expectation, :from => :to_not
66
+ end
67
+ end
68
+ end
69
+