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
@@ -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