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
@@ -40,6 +40,14 @@ module RSpec
40
40
  end
41
41
  end
42
42
 
43
+ class ProcWithBlock < Struct.new(:object, :method_name)
44
+
45
+ def call(*args, &block)
46
+ self.object.__send__(:method_missing, self.method_name, *args, &block)
47
+ end
48
+
49
+ end
50
+
43
51
  # @private
44
52
  def original_method
45
53
  if @method_stasher.method_is_stashed?
@@ -55,48 +63,58 @@ module RSpec
55
63
  # object rather than the proxy method.
56
64
  meth
57
65
  else
58
- begin
59
- # Example: an instance method defined on one of @object's ancestors.
60
- original_method_from_ancestor(object_singleton_class.ancestors)
61
- rescue NameError
62
- raise unless @object.respond_to?(:superclass)
63
-
64
- # Example: a singleton method defined on @object's superclass.
65
- #
66
- # Note: we have to give precedence to instance methods
67
- # defined on @object's class, because in a case like:
68
- #
69
- # `klass.should_receive(:new).and_call_original`
70
- #
71
- # ...we want `Class#new` bound to `klass` (which will return
72
- # an instance of `klass`), not `klass.superclass.new` (which
73
- # would return an instance of `klass.superclass`).
74
- original_method_from_superclass
75
- end
66
+ # Example: an instance method defined on one of @object's ancestors.
67
+ original_method_from_ancestry
76
68
  end
77
69
  rescue NameError
78
70
  # We have no way of knowing if the object's method_missing
79
71
  # will handle this message or not...but we can at least try.
80
72
  # If it's not handled, a `NoMethodError` will be raised, just
81
73
  # like normally.
82
- Proc.new do |*args, &block|
83
- @object.__send__(:method_missing, @method_name, *args, &block)
84
- end
74
+ ProcWithBlock.new(@object,@method_name)
85
75
  end
86
76
 
87
77
  def original_unrecorded_any_instance_method
88
78
  return nil unless any_instance_class_recorder_observing_method?(@object.class)
89
- alias_name = @object.class.__recorder.build_alias_method_name(@method_name)
79
+ alias_name = ::RSpec::Mocks.any_instance_recorder_for(@object.class).build_alias_method_name(@method_name)
90
80
  @object.method(alias_name)
91
81
  end
92
82
 
93
83
  def any_instance_class_recorder_observing_method?(klass)
94
- return true if klass.__recorder.already_observing?(@method_name)
84
+ return true if ::RSpec::Mocks.any_instance_recorder_for(klass).already_observing?(@method_name)
95
85
  superklass = klass.superclass
96
86
  return false if superklass.nil?
97
87
  any_instance_class_recorder_observing_method?(superklass)
98
88
  end
99
89
 
90
+ our_singleton_class = class << self; self; end
91
+ if our_singleton_class.ancestors.include? our_singleton_class
92
+ # In Ruby 2.1, ancestors include the correct ancestors, including the singleton classes
93
+ def original_method_from_ancestry
94
+ # Lookup in the ancestry, skipping over the singleton class itself
95
+ original_method_from_ancestor(object_singleton_class.ancestors.drop(1))
96
+ end
97
+ else
98
+ # @private
99
+ def original_method_from_ancestry
100
+ original_method_from_ancestor(object_singleton_class.ancestors)
101
+ rescue NameError
102
+ raise unless @object.respond_to?(:superclass)
103
+
104
+ # Example: a singleton method defined on @object's superclass.
105
+ #
106
+ # Note: we have to give precedence to instance methods
107
+ # defined on @object's class, because in a case like:
108
+ #
109
+ # `klass.should_receive(:new).and_call_original`
110
+ #
111
+ # ...we want `Class#new` bound to `klass` (which will return
112
+ # an instance of `klass`), not `klass.superclass.new` (which
113
+ # would return an instance of `klass.superclass`).
114
+ original_method_from_superclass
115
+ end
116
+ end
117
+
100
118
  def original_method_from_ancestor(ancestors)
101
119
  klass, *rest = ancestors
102
120
  klass.instance_method(@method_name).bind(@object)
@@ -142,8 +160,6 @@ module RSpec
142
160
 
143
161
  # @private
144
162
  def configure_method
145
- RSpec::Mocks::space.add(@object) if RSpec::Mocks::space
146
- warn_if_nil_class
147
163
  @original_visibility = visibility_for_method
148
164
  @method_stasher.stash unless @method_is_proxied
149
165
  define_proxy_method
@@ -155,7 +171,7 @@ module RSpec
155
171
 
156
172
  object_singleton_class.class_eval <<-EOF, __FILE__, __LINE__ + 1
157
173
  def #{@method_name}(*args, &block)
158
- __mock_proxy.message_received :#{@method_name}, *args, &block
174
+ ::RSpec::Mocks.proxy_for(self).message_received :#{@method_name}, *args, &block
159
175
  end
160
176
  #{visibility_for_method}
161
177
  EOF
@@ -191,7 +207,6 @@ module RSpec
191
207
 
192
208
  # @private
193
209
  def reset
194
- reset_nil_expectations_warning
195
210
  restore_original_method
196
211
  clear
197
212
  end
@@ -220,6 +235,12 @@ module RSpec
220
235
  expectation
221
236
  end
222
237
 
238
+ # @private
239
+ def build_expectation(error_generator, expectation_ordering)
240
+ expected_from = IGNORED_BACKTRACE_LINE
241
+ MessageExpectation.new(error_generator, expectation_ordering, expected_from, self)
242
+ end
243
+
223
244
  # @private
224
245
  def add_stub(error_generator, expectation_ordering, expected_from, opts={}, &implementation)
225
246
  configure_method
@@ -241,27 +262,13 @@ module RSpec
241
262
  expectations.empty? ? reset : stubs.clear
242
263
  end
243
264
 
244
- # @private
245
- def proxy_for_nil_class?
246
- NilClass === @object
247
- end
248
-
249
- # @private
250
- def warn_if_nil_class
251
- if proxy_for_nil_class? & RSpec::Mocks::Proxy.warn_about_expectations_on_nil
252
- Kernel.warn("An expectation of :#{@method_name} was set on nil. Called from #{caller[4]}. Use allow_message_expectations_on_nil to disable warnings.")
253
- end
254
- end
255
-
256
265
  # @private
257
266
  def raise_method_not_stubbed_error
258
- raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
267
+ raise MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
259
268
  end
260
269
 
261
270
  # @private
262
- def reset_nil_expectations_warning
263
- RSpec::Mocks::Proxy.warn_about_expectations_on_nil = true if proxy_for_nil_class?
264
- end
271
+ IGNORED_BACKTRACE_LINE = 'this backtrace line is ignored'
265
272
  end
266
273
  end
267
274
  end
@@ -83,6 +83,9 @@ module RSpec
83
83
  # @api private
84
84
  def initialize(name)
85
85
  @name = name
86
+ @previously_defined = false
87
+ @stubbed = false
88
+ @hidden = false
86
89
  end
87
90
 
88
91
  # @return [String] The fully qualified name of the constant.
@@ -222,7 +225,7 @@ module RSpec
222
225
  @context = recursive_const_get(@context_parts.join('::'))
223
226
  @original_value = get_const_defined_on(@context, @const_name)
224
227
 
225
- @context.send(:remove_const, @const_name)
228
+ @context.__send__(:remove_const, @const_name)
226
229
  end
227
230
 
228
231
  def to_constant
@@ -248,7 +251,7 @@ module RSpec
248
251
 
249
252
  constants_to_transfer = verify_constants_to_transfer!
250
253
 
251
- @context.send(:remove_const, @const_name)
254
+ @context.__send__(:remove_const, @const_name)
252
255
  @context.const_set(@const_name, @mutated_value)
253
256
 
254
257
  transfer_nested_constants(constants_to_transfer)
@@ -263,7 +266,7 @@ module RSpec
263
266
  end
264
267
 
265
268
  def rspec_reset
266
- @context.send(:remove_const, @const_name)
269
+ @context.__send__(:remove_const, @const_name)
267
270
  @context.const_set(@const_name, @original_value)
268
271
  end
269
272
 
@@ -333,7 +336,7 @@ module RSpec
333
336
  end
334
337
 
335
338
  def rspec_reset
336
- @deepest_defined_const.send(:remove_const, @const_to_remove)
339
+ @deepest_defined_const.__send__(:remove_const, @const_to_remove)
337
340
  end
338
341
  end
339
342
 
@@ -345,27 +348,13 @@ module RSpec
345
348
  def self.mutate(mutator)
346
349
  register_mutator(mutator)
347
350
  mutator.mutate
348
- ensure_registered_with_mocks_space
349
- end
350
-
351
- # Ensures the constant stubbing is registered with
352
- # rspec-mocks space so that stubbed constants can
353
- # be restored when examples finish.
354
- #
355
- # @api private
356
- def self.ensure_registered_with_mocks_space
357
- return if defined?(@registered_with_mocks_space) && @registered_with_mocks_space
358
- ::RSpec::Mocks.space.add(self)
359
- @registered_with_mocks_space = true
360
351
  end
361
352
 
362
353
  # Resets all stubbed constants. This is called automatically
363
354
  # by rspec-mocks when an example finishes.
364
355
  #
365
356
  # @api private
366
- def self.rspec_reset
367
- @registered_with_mocks_space = false
368
-
357
+ def self.reset_all
369
358
  # We use reverse order so that if the same constant
370
359
  # was stubbed multiple times, the original value gets
371
360
  # properly restored.
@@ -412,4 +401,3 @@ module RSpec
412
401
  ConstantStubber = ConstantMutator
413
402
  end
414
403
  end
415
-
@@ -2,31 +2,6 @@ module RSpec
2
2
  module Mocks
3
3
  # @private
4
4
  class Proxy
5
- class << self
6
- # @private
7
- def warn_about_expectations_on_nil
8
- defined?(@warn_about_expectations_on_nil) ? @warn_about_expectations_on_nil : true
9
- end
10
-
11
- # @private
12
- def warn_about_expectations_on_nil=(new_value)
13
- @warn_about_expectations_on_nil = new_value
14
- end
15
-
16
- # @private
17
- def allow_message_expectations_on_nil
18
- @warn_about_expectations_on_nil = false
19
-
20
- # ensure nil.rspec_verify is called even if an expectation is not set in the example
21
- # otherwise the allowance would effect subsequent examples
22
- RSpec::Mocks::space.add(nil) unless RSpec::Mocks::space.nil?
23
- end
24
-
25
- # @private
26
- def allow_message_expectations_on_nil?
27
- !warn_about_expectations_on_nil
28
- end
29
- end
30
5
 
31
6
  # @private
32
7
  def initialize(object, name=nil, options={})
@@ -81,6 +56,45 @@ module RSpec
81
56
  method_double[method_name].add_negative_expectation @error_generator, @expectation_ordering, location, &implementation
82
57
  end
83
58
 
59
+ # @private
60
+ def build_expectation(method_name)
61
+ meth_double = method_double[method_name]
62
+
63
+ meth_double.build_expectation(
64
+ @error_generator,
65
+ @expectation_ordering
66
+ )
67
+ end
68
+
69
+ # @private
70
+ def replay_received_message_on(expectation)
71
+ expected_method_name = expectation.message
72
+ meth_double = method_double[expected_method_name]
73
+
74
+ if meth_double.expectations.any?
75
+ @error_generator.raise_expectation_on_mocked_method(expected_method_name)
76
+ end
77
+
78
+ unless null_object? || meth_double.stubs.any?
79
+ @error_generator.raise_expectation_on_unstubbed_method(expected_method_name)
80
+ end
81
+
82
+ @messages_received.each do |(actual_method_name, args, _)|
83
+ if expectation.matches?(actual_method_name, *args)
84
+ expectation.invoke(nil)
85
+ end
86
+ end
87
+ end
88
+
89
+ # @private
90
+ def check_for_unexpected_arguments(expectation)
91
+ @messages_received.each do |(method_name, args, _)|
92
+ if expectation.matches_name_but_not_args(method_name, *args)
93
+ raise_unexpected_message_args_error(expectation, *args)
94
+ end
95
+ end
96
+ end
97
+
84
98
  # @private
85
99
  def add_stub(location, method_name, opts={}, &implementation)
86
100
  method_double[method_name].add_stub @error_generator, @expectation_ordering, location, opts, &implementation
@@ -101,6 +115,7 @@ module RSpec
101
115
  # @private
102
116
  def reset
103
117
  method_doubles.each {|d| d.reset}
118
+ @messages_received.clear
104
119
  end
105
120
 
106
121
  # @private
@@ -120,6 +135,7 @@ module RSpec
120
135
 
121
136
  # @private
122
137
  def message_received(message, *args, &block)
138
+ record_message_received message, *args, &block
123
139
  expectation = find_matching_expectation(message, *args)
124
140
  stub = find_matching_method_stub(message, *args)
125
141
 
@@ -0,0 +1,36 @@
1
+ module RSpec
2
+ module Mocks
3
+ # @private
4
+ class ProxyForNil < Proxy
5
+
6
+ def initialize
7
+ @warn_about_expectations = true
8
+ super nil
9
+ end
10
+ attr_accessor :warn_about_expectations
11
+ alias warn_about_expectations? warn_about_expectations
12
+
13
+ def add_message_expectation(location, method_name, opts={}, &block)
14
+ warn(method_name) if warn_about_expectations?
15
+ super
16
+ end
17
+
18
+ def add_negative_message_expectation(location, method_name, &implementation)
19
+ warn(method_name) if warn_about_expectations?
20
+ super
21
+ end
22
+
23
+ def add_stub(location, method_name, opts={}, &implementation)
24
+ warn(method_name) if warn_about_expectations?
25
+ super
26
+ end
27
+
28
+ private
29
+
30
+ def warn method_name
31
+ Kernel.warn("An expectation of :#{method_name} was set on nil. Called from #{caller[2]}. Use allow_message_expectations_on_nil to disable warnings.")
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -2,21 +2,32 @@ module RSpec
2
2
  module Mocks
3
3
  # @api private
4
4
  class Space
5
- def add(obj)
6
- receivers << obj unless receivers.detect {|m| m.equal? obj}
5
+ attr_reader :proxies, :any_instance_recorders
6
+
7
+ def initialize
8
+ @proxies = {}
9
+ @any_instance_recorders = {}
7
10
  end
8
11
 
9
12
  def verify_all
10
- receivers.each do |mock|
11
- mock.rspec_verify
13
+ proxies.each_value do |object|
14
+ object.verify
15
+ end
16
+
17
+ any_instance_recorders.each_value do |recorder|
18
+ recorder.verify
12
19
  end
13
20
  end
14
21
 
15
22
  def reset_all
16
- receivers.each do |mock|
17
- mock.rspec_reset
23
+ ConstantMutator.reset_all
24
+
25
+ proxies.each_value do |object|
26
+ object.reset
18
27
  end
19
- receivers.clear
28
+
29
+ proxies.clear
30
+ any_instance_recorders.clear
20
31
  expectation_ordering.clear
21
32
  end
22
33
 
@@ -24,10 +35,52 @@ module RSpec
24
35
  @expectation_ordering ||= OrderGroup.new
25
36
  end
26
37
 
27
- private
28
-
29
- def receivers
30
- @receivers ||= []
38
+ def any_instance_recorder_for(klass)
39
+ id = klass.__id__
40
+ any_instance_recorders.fetch(id) do
41
+ any_instance_recorders[id] = AnyInstance::Recorder.new(klass)
42
+ end
43
+ end
44
+
45
+ def remove_any_instance_recorder_for(klass)
46
+ any_instance_recorders.delete(klass.__id__)
47
+ end
48
+
49
+ def proxy_for(object)
50
+ id = id_for(object)
51
+ proxies.fetch(id) do
52
+ proxies[id] = case object
53
+ when NilClass then ProxyForNil.new
54
+ when TestDouble then object.__build_mock_proxy
55
+ else
56
+ Proxy.new(object)
57
+ end
58
+ end
59
+ end
60
+
61
+ alias ensure_registered proxy_for
62
+
63
+ def registered?(object)
64
+ proxies.has_key?(id_for object)
65
+ end
66
+
67
+ if defined?(::BasicObject) && !::BasicObject.method_defined?(:__id__) # for 1.9.2
68
+ require 'securerandom'
69
+
70
+ def id_for(object)
71
+ id = object.__id__
72
+
73
+ return id if object.equal?(::ObjectSpace._id2ref(id))
74
+ # this suggests that object.__id__ is proxying through to some wrapped object
75
+
76
+ object.instance_eval do
77
+ @__id_for_rspec_mocks_space ||= ::SecureRandom.uuid
78
+ end
79
+ end
80
+ else
81
+ def id_for(object)
82
+ object.__id__
83
+ end
31
84
  end
32
85
  end
33
86
  end