rspec-mocks 2.13.1 → 2.14.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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
+