rspec-mocks 3.0.4 → 3.12.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.document +1 -1
  4. data/.yardopts +1 -1
  5. data/Changelog.md +512 -2
  6. data/{License.txt → LICENSE.md} +5 -4
  7. data/README.md +113 -30
  8. data/lib/rspec/mocks/any_instance/chain.rb +5 -3
  9. data/lib/rspec/mocks/any_instance/error_generator.rb +31 -0
  10. data/lib/rspec/mocks/any_instance/expect_chain_chain.rb +1 -5
  11. data/lib/rspec/mocks/any_instance/expectation_chain.rb +9 -8
  12. data/lib/rspec/mocks/any_instance/message_chains.rb +7 -8
  13. data/lib/rspec/mocks/any_instance/proxy.rb +14 -5
  14. data/lib/rspec/mocks/any_instance/recorder.rb +61 -31
  15. data/lib/rspec/mocks/any_instance/stub_chain.rb +15 -11
  16. data/lib/rspec/mocks/any_instance/stub_chain_chain.rb +1 -5
  17. data/lib/rspec/mocks/any_instance.rb +1 -0
  18. data/lib/rspec/mocks/argument_list_matcher.rb +55 -10
  19. data/lib/rspec/mocks/argument_matchers.rb +88 -30
  20. data/lib/rspec/mocks/configuration.rb +61 -13
  21. data/lib/rspec/mocks/error_generator.rb +250 -107
  22. data/lib/rspec/mocks/example_methods.rb +151 -28
  23. data/lib/rspec/mocks/instance_method_stasher.rb +17 -6
  24. data/lib/rspec/mocks/matchers/have_received.rb +50 -20
  25. data/lib/rspec/mocks/matchers/receive.rb +39 -11
  26. data/lib/rspec/mocks/matchers/receive_message_chain.rb +22 -7
  27. data/lib/rspec/mocks/matchers/receive_messages.rb +12 -7
  28. data/lib/rspec/mocks/message_chain.rb +3 -7
  29. data/lib/rspec/mocks/message_expectation.rb +466 -307
  30. data/lib/rspec/mocks/method_double.rb +88 -29
  31. data/lib/rspec/mocks/method_reference.rb +85 -25
  32. data/lib/rspec/mocks/minitest_integration.rb +68 -0
  33. data/lib/rspec/mocks/mutate_const.rb +50 -109
  34. data/lib/rspec/mocks/object_reference.rb +89 -32
  35. data/lib/rspec/mocks/order_group.rb +4 -5
  36. data/lib/rspec/mocks/proxy.rb +156 -60
  37. data/lib/rspec/mocks/space.rb +52 -35
  38. data/lib/rspec/mocks/standalone.rb +1 -1
  39. data/lib/rspec/mocks/syntax.rb +26 -30
  40. data/lib/rspec/mocks/targets.rb +55 -28
  41. data/lib/rspec/mocks/test_double.rb +43 -7
  42. data/lib/rspec/mocks/verifying_double.rb +27 -33
  43. data/lib/rspec/mocks/{verifying_message_expecation.rb → verifying_message_expectation.rb} +11 -16
  44. data/lib/rspec/mocks/verifying_proxy.rb +77 -26
  45. data/lib/rspec/mocks/version.rb +1 -1
  46. data/lib/rspec/mocks.rb +8 -1
  47. data.tar.gz.sig +0 -0
  48. metadata +80 -43
  49. metadata.gz.sig +0 -0
@@ -1,3 +1,5 @@
1
+ RSpec::Support.require_rspec_support "object_formatter"
2
+
1
3
  module RSpec
2
4
  module Mocks
3
5
  # Raised when a message expectation is not satisfied.
@@ -10,6 +12,19 @@ module RSpec
10
12
  # Raised when doubles or partial doubles are used outside of the per-test lifecycle.
11
13
  OutsideOfExampleError = Class.new(StandardError)
12
14
 
15
+ # Raised when an expectation customization method (e.g. `with`,
16
+ # `and_return`) is called on a message expectation which has already been
17
+ # invoked.
18
+ MockExpectationAlreadyInvokedError = Class.new(Exception)
19
+
20
+ # Raised for situations that RSpec cannot support due to mutations made
21
+ # externally on arguments that RSpec is holding onto to use for later
22
+ # comparisons.
23
+ #
24
+ # @deprecated We no longer raise this error but the constant remains until
25
+ # RSpec 4 for SemVer reasons.
26
+ CannotSupportArgMutationsError = Class.new(StandardError)
27
+
13
28
  # @private
14
29
  UnsupportedMatcherError = Class.new(StandardError)
15
30
  # @private
@@ -21,9 +36,8 @@ module RSpec
21
36
  class ErrorGenerator
22
37
  attr_writer :opts
23
38
 
24
- def initialize(target, name)
39
+ def initialize(target=nil)
25
40
  @target = target
26
- @name = name
27
41
  end
28
42
 
29
43
  # @private
@@ -32,44 +46,65 @@ module RSpec
32
46
  end
33
47
 
34
48
  # @private
35
- def raise_unexpected_message_error(message, *args)
36
- __raise "#{intro} received unexpected message :#{message}#{arg_message(*args)}"
49
+ def raise_unexpected_message_error(message, args)
50
+ __raise "#{intro} received unexpected message :#{message} with #{format_args(args)}"
37
51
  end
38
52
 
39
53
  # @private
40
- def raise_unexpected_message_args_error(expectation, *args)
41
- expected_args = format_args(*expectation.expected_args)
42
- actual_args = format_received_args(*args)
43
- __raise "#{intro} received #{expectation.message.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}"
54
+ def raise_unexpected_message_args_error(expectation, args_for_multiple_calls, source_id=nil)
55
+ __raise error_message(expectation, args_for_multiple_calls), nil, source_id
44
56
  end
45
57
 
46
58
  # @private
47
- def raise_missing_default_stub_error(expectation, *args)
48
- expected_args = format_args(*expectation.expected_args)
49
- actual_args = format_received_args(*args)
50
- __raise "#{intro} received #{expectation.message.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}\n Please stub a default value first if message might be received with other args as well. \n"
59
+ def raise_missing_default_stub_error(expectation, args_for_multiple_calls)
60
+ __raise(
61
+ error_message(expectation, args_for_multiple_calls) +
62
+ "\n Please stub a default value first if message might be received with other args as well. \n"
63
+ )
51
64
  end
52
65
 
53
66
  # @private
54
- def raise_similar_message_args_error(expectation, *args_for_multiple_calls)
55
- expected_args = format_args(*expectation.expected_args)
56
- actual_args = args_for_multiple_calls.collect {|a| format_received_args(*a)}.join(", ")
57
- __raise "#{intro} received #{expectation.message.inspect} with unexpected arguments\n expected: #{expected_args}\n got: #{actual_args}"
67
+ def raise_similar_message_args_error(expectation, args_for_multiple_calls, backtrace_line=nil)
68
+ __raise error_message(expectation, args_for_multiple_calls), backtrace_line
58
69
  end
59
70
 
71
+ def default_error_message(expectation, expected_args, actual_args)
72
+ "#{intro} received #{expectation.message.inspect} #{unexpected_arguments_message(expected_args, actual_args)}".dup
73
+ end
74
+
75
+ # rubocop:disable Metrics/ParameterLists
60
76
  # @private
61
- def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, *args)
77
+ def raise_expectation_error(message, expected_received_count, argument_list_matcher,
78
+ actual_received_count, expectation_count_type, args,
79
+ backtrace_line=nil, source_id=nil)
62
80
  expected_part = expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher)
63
- received_part = received_part_of_expectation_error(actual_received_count, *args)
64
- __raise "(#{intro}).#{message}#{format_args(*args)}\n #{expected_part}\n #{received_part}"
81
+ received_part = received_part_of_expectation_error(actual_received_count, args)
82
+ __raise "(#{intro(:unwrapped)}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}", backtrace_line, source_id
65
83
  end
84
+ # rubocop:enable Metrics/ParameterLists
66
85
 
67
86
  # @private
68
- def raise_unimplemented_error(doubled_module, method_name)
69
- __raise "%s does not implement: %s" % [
70
- doubled_module.description,
71
- method_name
72
- ]
87
+ def raise_unimplemented_error(doubled_module, method_name, object)
88
+ message = case object
89
+ when InstanceVerifyingDouble
90
+ "the %s class does not implement the instance method: %s".dup <<
91
+ if ObjectMethodReference.for(doubled_module, method_name).implemented?
92
+ ". Perhaps you meant to use `class_double` instead?"
93
+ else
94
+ ""
95
+ end
96
+ when ClassVerifyingDouble
97
+ "the %s class does not implement the class method: %s".dup <<
98
+ if InstanceMethodReference.for(doubled_module, method_name).implemented?
99
+ ". Perhaps you meant to use `instance_double` instead?"
100
+ else
101
+ ""
102
+ end
103
+ else
104
+ "%s does not implement: %s"
105
+ end
106
+
107
+ __raise message % [doubled_module.description, method_name]
73
108
  end
74
109
 
75
110
  # @private
@@ -87,161 +122,269 @@ module RSpec
87
122
  # @private
88
123
  def raise_expired_test_double_error
89
124
  raise ExpiredTestDoubleError,
90
- "#{intro} was originally created in one example but has leaked into " +
91
- "another example and can no longer be used. rspec-mocks' doubles are " +
92
- "designed to only last for one example, and you need to create a new " +
93
- "one in each example you wish to use it for."
125
+ "#{intro} was originally created in one example but has leaked into " \
126
+ "another example and can no longer be used. rspec-mocks' doubles are " \
127
+ "designed to only last for one example, and you need to create a new " \
128
+ "one in each example you wish to use it for."
94
129
  end
95
130
 
96
131
  # @private
97
- def received_part_of_expectation_error(actual_received_count, *args)
98
- "received: #{count_message(actual_received_count)}" +
99
- actual_method_call_args_description(actual_received_count, args)
132
+ def describe_expectation(verb, message, expected_received_count, _actual_received_count, args)
133
+ "#{verb} #{message}#{format_args(args)} #{count_message(expected_received_count)}"
100
134
  end
101
135
 
102
136
  # @private
103
- def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher)
104
- "expected: #{count_message(expected_received_count, expectation_count_type)}" +
105
- expected_method_call_args_description(argument_list_matcher.expected_args)
137
+ def raise_out_of_order_error(message)
138
+ __raise "#{intro} received :#{message} out of order"
106
139
  end
107
140
 
108
141
  # @private
109
- def actual_method_call_args_description(count, args)
110
- method_call_args_description(args) ||
111
- if count > 0 && args.length > 0
112
- " with arguments: #{args.inspect.gsub(/\A\[(.+)\]\z/, '(\1)')}"
113
- else
114
- ""
115
- end
142
+ def raise_missing_block_error(args_to_yield)
143
+ __raise "#{intro} asked to yield |#{arg_list(args_to_yield)}| but no block was passed"
116
144
  end
117
145
 
118
146
  # @private
119
- def expected_method_call_args_description(args)
120
- method_call_args_description(args) ||
121
- if args.length > 0
122
- " with arguments: #{format_args(*args)}"
123
- else
124
- ""
125
- end
147
+ def raise_wrong_arity_error(args_to_yield, signature)
148
+ __raise "#{intro} yielded |#{arg_list(args_to_yield)}| to block with #{signature.description}"
126
149
  end
127
150
 
128
151
  # @private
129
- def method_call_args_description(args)
130
- case args.first
131
- when ArgumentMatchers::AnyArgsMatcher
132
- return " with any arguments"
133
- when ArgumentMatchers::NoArgsMatcher
134
- return " with no arguments"
135
- end
152
+ def raise_only_valid_on_a_partial_double(method)
153
+ __raise "#{intro} is a pure test double. `#{method}` is only " \
154
+ "available on a partial double."
136
155
  end
137
156
 
138
157
  # @private
139
- def describe_expectation(message, expected_received_count, actual_received_count, *args)
140
- "have received #{message}#{format_args(*args)} #{count_message(expected_received_count)}"
158
+ def raise_expectation_on_unstubbed_method(method)
159
+ __raise "#{intro} expected to have received #{method}, but that " \
160
+ "object is not a spy or method has not been stubbed."
141
161
  end
142
162
 
143
163
  # @private
144
- def raise_out_of_order_error(message)
145
- __raise "#{intro} received :#{message} out of order"
164
+ def raise_expectation_on_mocked_method(method)
165
+ __raise "#{intro} expected to have received #{method}, but that " \
166
+ "method has been mocked instead of stubbed or spied."
146
167
  end
147
168
 
148
169
  # @private
149
- def raise_block_failed_error(message, detail)
150
- __raise "#{intro} received :#{message} but passed block failed with: #{detail}"
170
+ def raise_double_negation_error(wrapped_expression)
171
+ __raise "Isn't life confusing enough? You've already set a " \
172
+ "negative message expectation and now you are trying to " \
173
+ "negate it again with `never`. What does an expression like " \
174
+ "`#{wrapped_expression}.not_to receive(:msg).never` even mean?"
151
175
  end
152
176
 
153
177
  # @private
154
- def raise_missing_block_error(args_to_yield)
155
- __raise "#{intro} asked to yield |#{arg_list(*args_to_yield)}| but no block was passed"
178
+ def raise_verifying_double_not_defined_error(ref)
179
+ notify(VerifyingDoubleNotDefinedError.new(
180
+ "#{ref.description.inspect} is not a defined constant. " \
181
+ "Perhaps you misspelt it? " \
182
+ "Disable check with `verify_doubled_constant_names` configuration option."
183
+ ))
156
184
  end
157
185
 
158
186
  # @private
159
- def raise_wrong_arity_error(args_to_yield, signature)
160
- __raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with #{signature.description}"
187
+ def raise_have_received_disallowed(type, reason)
188
+ __raise "Using #{type}(...) with the `have_received` " \
189
+ "matcher is not supported#{reason}."
161
190
  end
162
191
 
163
192
  # @private
164
- def raise_only_valid_on_a_partial_double(method)
165
- __raise "#{intro} is a pure test double. `#{method}` is only " +
166
- "available on a partial double."
193
+ def raise_cant_constrain_count_for_negated_have_received_error(count_constraint)
194
+ __raise "can't use #{count_constraint} when negative"
167
195
  end
168
196
 
169
197
  # @private
170
- def raise_expectation_on_unstubbed_method(method)
171
- __raise "#{intro} expected to have received #{method}, but that " +
172
- "method has not been stubbed."
198
+ def raise_method_not_stubbed_error(method_name)
199
+ __raise "The method `#{method_name}` was not stubbed or was already unstubbed"
173
200
  end
174
201
 
175
202
  # @private
176
- def raise_expectation_on_mocked_method(method)
177
- __raise "#{intro} expected to have received #{method}, but that " +
178
- "method has been mocked instead of stubbed."
203
+ def raise_already_invoked_error(message, calling_customization)
204
+ error_message = "The message expectation for #{intro}.#{message} has already been invoked " \
205
+ "and cannot be modified further (e.g. using `#{calling_customization}`). All message expectation " \
206
+ "customizations must be applied before it is used for the first time."
207
+
208
+ notify MockExpectationAlreadyInvokedError.new(error_message)
179
209
  end
180
210
 
181
- def self.raise_double_negation_error(wrapped_expression)
182
- raise "Isn't life confusing enough? You've already set a " +
183
- "negative message expectation and now you are trying to " +
184
- "negate it again with `never`. What does an expression like " +
185
- "`#{wrapped_expression}.not_to receive(:msg).never` even mean?"
211
+ def raise_expectation_on_nil_error(method_name)
212
+ __raise expectation_on_nil_message(method_name)
186
213
  end
187
214
 
188
- private
215
+ def expectation_on_nil_message(method_name)
216
+ "An expectation of `:#{method_name}` was set on `nil`. " \
217
+ "To allow expectations on `nil` and suppress this message, set `RSpec::Mocks.configuration.allow_message_expectations_on_nil` to `true`. " \
218
+ "To disallow expectations on `nil`, set `RSpec::Mocks.configuration.allow_message_expectations_on_nil` to `false`"
219
+ end
220
+
221
+ # @private
222
+ def intro(unwrapped=false)
223
+ case @target
224
+ when TestDouble then TestDoubleFormatter.format(@target, unwrapped)
225
+ when Class then
226
+ formatted = "#{@target.inspect} (class)"
227
+ return formatted if unwrapped
228
+ "#<#{formatted}>"
229
+ when NilClass then "nil"
230
+ else @target.inspect
231
+ end
232
+ end
189
233
 
190
- def intro
191
- if @name
192
- "Double #{@name.inspect}"
193
- elsif TestDouble === @target
194
- "Double"
195
- elsif Class === @target
196
- "<#{@target.inspect} (class)>"
197
- elsif @target
198
- @target
234
+ # @private
235
+ def method_call_args_description(args, generic_prefix=" with arguments: ", matcher_prefix=" with ")
236
+ case args.first
237
+ when ArgumentMatchers::AnyArgsMatcher then "#{matcher_prefix}any arguments"
238
+ when ArgumentMatchers::NoArgsMatcher then "#{matcher_prefix}no arguments"
199
239
  else
200
- "nil"
240
+ if yield
241
+ "#{generic_prefix}#{format_args(args)}"
242
+ else
243
+ ""
244
+ end
201
245
  end
202
246
  end
203
247
 
204
- def __raise(message)
205
- message = opts[:message] unless opts[:message].nil?
206
- Kernel::raise(RSpec::Mocks::MockExpectationError, message)
248
+ private
249
+
250
+ def received_part_of_expectation_error(actual_received_count, args)
251
+ "received: #{count_message(actual_received_count)}" +
252
+ method_call_args_description(args) do
253
+ actual_received_count > 0 && args.length > 0
254
+ end
255
+ end
256
+
257
+ def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher)
258
+ "expected: #{count_message(expected_received_count, expectation_count_type)}" +
259
+ method_call_args_description(argument_list_matcher.expected_args) do
260
+ argument_list_matcher.expected_args.length > 0
261
+ end
262
+ end
263
+
264
+ def unexpected_arguments_message(expected_args_string, actual_args_string)
265
+ "with unexpected arguments\n expected: #{expected_args_string}\n got: #{actual_args_string}"
266
+ end
267
+
268
+ def error_message(expectation, args_for_multiple_calls)
269
+ expected_args = format_args(expectation.expected_args)
270
+ actual_args = format_received_args(args_for_multiple_calls)
271
+
272
+ if RSpec::Support::RubyFeatures.distincts_kw_args_from_positional_hash?
273
+ expected_hash = expectation.expected_args.last
274
+ actual_hash = args_for_multiple_calls.last.last
275
+ if Hash === expected_hash && Hash === actual_hash &&
276
+ (Hash.ruby2_keywords_hash?(expected_hash) != Hash.ruby2_keywords_hash?(actual_hash))
277
+
278
+ actual_description = Hash.ruby2_keywords_hash?(actual_hash) ? " (keyword arguments)" : " (options hash)"
279
+ expected_description = Hash.ruby2_keywords_hash?(expected_hash) ? " (keyword arguments)" : " (options hash)"
280
+
281
+ if actual_description != expected_description
282
+ actual_args += actual_description
283
+ expected_args += expected_description
284
+ end
285
+ end
286
+ end
287
+
288
+ message = default_error_message(expectation, expected_args, actual_args)
289
+
290
+ if args_for_multiple_calls.one?
291
+ diff = diff_message(expectation.expected_args, args_for_multiple_calls.first)
292
+ if RSpec::Mocks.configuration.color?
293
+ message << "\nDiff:#{diff}" unless diff.gsub(/\e\[\d+m/, '').strip.empty?
294
+ else
295
+ message << "\nDiff:#{diff}" unless diff.strip.empty?
296
+ end
297
+ end
298
+
299
+ message
300
+ end
301
+
302
+ def diff_message(expected_args, actual_args)
303
+ formatted_expected_args = expected_args.map do |x|
304
+ RSpec::Support.rspec_description_for_object(x)
305
+ end
306
+
307
+ formatted_expected_args, actual_args = unpack_string_args(formatted_expected_args, actual_args)
308
+
309
+ differ.diff(actual_args, formatted_expected_args)
310
+ end
311
+
312
+ def unpack_string_args(formatted_expected_args, actual_args)
313
+ if [formatted_expected_args, actual_args].all? { |x| list_of_exactly_one_string?(x) }
314
+ [formatted_expected_args.first, actual_args.first]
315
+ else
316
+ [formatted_expected_args, actual_args]
317
+ end
318
+ end
319
+
320
+ def list_of_exactly_one_string?(args)
321
+ Array === args && args.count == 1 && String === args.first
207
322
  end
208
323
 
209
- def arg_message(*args)
210
- " with " + format_args(*args)
324
+ def differ
325
+ RSpec::Support::Differ.new(:color => RSpec::Mocks.configuration.color?)
211
326
  end
212
327
 
213
- def format_args(*args)
214
- args.empty? ? "(no args)" : "(" + arg_list(*args) + ")"
328
+ def __raise(message, backtrace_line=nil, source_id=nil)
329
+ message = opts[:message] unless opts[:message].nil?
330
+ exception = RSpec::Mocks::MockExpectationError.new(message)
331
+ prepend_to_backtrace(exception, backtrace_line) if backtrace_line
332
+ notify exception, :source_id => source_id
215
333
  end
216
334
 
217
- def arg_list(*args)
218
- args.collect {|arg| arg_has_valid_description(arg) ? arg.description : arg.inspect }.join(", ")
335
+ if RSpec::Support::Ruby.jruby?
336
+ def prepend_to_backtrace(exception, line)
337
+ raise exception
338
+ rescue RSpec::Mocks::MockExpectationError => with_backtrace
339
+ with_backtrace.backtrace.unshift(line)
340
+ end
341
+ else
342
+ def prepend_to_backtrace(exception, line)
343
+ exception.set_backtrace(caller.unshift line)
344
+ end
219
345
  end
220
346
 
221
- def arg_has_valid_description(arg)
222
- return false unless arg.respond_to?(:description)
347
+ def notify(*args)
348
+ RSpec::Support.notify_failure(*args)
349
+ end
223
350
 
224
- !arg.description.nil? && !arg.description.empty?
351
+ def format_args(args)
352
+ return "(no args)" if args.empty?
353
+ "(#{arg_list(args)})"
225
354
  end
226
355
 
227
- def format_received_args(*args)
228
- args.empty? ? "(no args)" : "(" + received_arg_list(*args) + ")"
356
+ def arg_list(args)
357
+ args.map { |arg| RSpec::Support::ObjectFormatter.format(arg) }.join(", ")
229
358
  end
230
359
 
231
- def received_arg_list(*args)
232
- args.collect(&:inspect).join(", ")
360
+ def format_received_args(args_for_multiple_calls)
361
+ grouped_args(args_for_multiple_calls).map do |args_for_one_call, index|
362
+ "#{format_args(args_for_one_call)}#{group_count(index, args_for_multiple_calls)}"
363
+ end.join("\n ")
233
364
  end
234
365
 
235
366
  def count_message(count, expectation_count_type=nil)
236
367
  return "at least #{times(count.abs)}" if count < 0 || expectation_count_type == :at_least
237
368
  return "at most #{times(count)}" if expectation_count_type == :at_most
238
- return times(count)
369
+ times(count)
239
370
  end
240
371
 
241
372
  def times(count)
242
373
  "#{count} time#{count == 1 ? '' : 's'}"
243
374
  end
244
375
 
376
+ def grouped_args(args)
377
+ Hash[args.group_by { |x| x }.map { |k, v| [k, v.count] }]
378
+ end
379
+
380
+ def group_count(index, args)
381
+ " (#{times(index)})" if args.size > 1 || index > 1
382
+ end
383
+ end
384
+
385
+ # @private
386
+ def self.error_generator
387
+ @error_generator ||= ErrorGenerator.new
245
388
  end
246
389
  end
247
390
  end