rspec-mocks 3.2.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e4228dd818e7161f972b35380ad510d2c9f42846
4
- data.tar.gz: 6b51cb642d8ab3a56c2bbacb6670bdbe0f5a8a80
3
+ metadata.gz: 0bb4586d03e4b2bf84d904af032165dfae270599
4
+ data.tar.gz: 6889f7727cf4545a05618a19c0f7ff35174718ff
5
5
  SHA512:
6
- metadata.gz: 33be4cf0a047a9e96eb84a057d59554ec8a0299cd67e8c8a26017f8d4b76f453edbe54754ea8c2362ef117cd1997ad32f0b6c36afb17e04434398f9d7609d601
7
- data.tar.gz: 15736e89331dd0b9bce1c3a7c96261cd692e4135bfbdc6a3f93adfd972f3453c3283311db099c8404495941dd86df668273ae62eee4e1f7ffcce5cc31aa1c29e
6
+ metadata.gz: 5bb9e03448dfde3e78aec7388dd7b49bccb8005ec49a7ab525b41818a6678e41568fa7946a4d443a8103058d77c4ae2760ac07eb69b0ef37dd39f197e580aa48
7
+ data.tar.gz: 00a6e20fb91ccb3dd7212ac5877e9b348609aede07ed8158aee6032f1e30d7d89a833a7d1107343e780da15daa0800d94985cdd151796434f5413c8663935fa0
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -1,3 +1,50 @@
1
+ ### 3.3.0 / 2015-06-12
2
+ [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.1...v3.3.0)
3
+
4
+ Enhancements:
5
+
6
+ * When stubbing `new` on `MyClass` or `class_double(MyClass)`, use the
7
+ method signature from `MyClass#initialize` to verify arguments.
8
+ (Myron Marston, #886)
9
+ * Use matcher descriptions when generating description of received arguments
10
+ for mock expectation failures. (Tim Wade, #891)
11
+ * Avoid loading `stringio` unnecessarily. (Myron Marston, #894)
12
+ * Verifying doubles failure messages now distinguish between class and instance
13
+ level methods. (Tim Wade, #896, #908)
14
+ * Improve mock expectation failure messages so that it combines both
15
+ number of times and the received arguments in the output. (John Ceh, #918)
16
+ * Improve how test doubles are represented in failure messages.
17
+ (Siva Gollapalli, Myron Marston, #932)
18
+ * Rename `RSpec::Mocks::Configuration#when_declaring_verifying_double` to
19
+ `RSpec::Mocks::Configuration#before_verifying_doubles` and utilise when
20
+ verifying partial doubles. (Jon Rowe, #940)
21
+ * Use rspec-support's `ObjectFormatter` for improved formatting of
22
+ arguments in failure messages so that, for example, full time
23
+ precisions is displayed for time objects. (Gavin Miller, Myron Marston, #955)
24
+
25
+ Bug Fixes:
26
+
27
+ * Ensure expectations that raise eagerly also raise during RSpec verification.
28
+ This means that if exceptions are caught inside test execution the test will
29
+ still fail. (Sam Phippen, #884)
30
+ * Fix `have_received(msg).with(args).exactly(n).times` and
31
+ `receive(msg).with(args).exactly(n).times` failure messages
32
+ for when the message was received the wrong number of times with
33
+ the specified args, and also received additional times with other
34
+ arguments. Previously it confusingly listed the arguments as being
35
+ mis-matched (even when the double was allowed to receive with any
36
+ args) rather than listing the count. (John Ceh, #918)
37
+ * Fix `any_args`/`anything` support so that we avoid calling `obj == anything`
38
+ on user objects that may have improperly implemented `==` in a way that
39
+ raises errors. (Myron Marston, #924)
40
+ * Fix edge case involving stubbing the same method on a class and a subclass
41
+ which previously hit a `NoMethodError` internally in RSpec. (Myron Marston #954)
42
+ * Fix edge case where the message received count would be incremented multiple
43
+ times for one failure. (Myron Marston, #957)
44
+ * Fix failure messages for when spies received the expected message with
45
+ different arguments and also received another message. (Maurício Linhares, #960)
46
+ * Silence whitespace-only diffs. (Myron Marston, #969)
47
+
1
48
  ### 3.2.1 / 2015-02-23
2
49
  [Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.2.0...v3.2.1)
3
50
 
data/README.md CHANGED
@@ -417,8 +417,8 @@ There are many different viewpoints about the meaning of mocks and stubs. If
417
417
  you are interested in learning more, here is some recommended reading:
418
418
 
419
419
  * Mock Objects: http://www.mockobjects.com/
420
- * Endo-Testing: http://stalatest.googlecode.com/svn/trunk/Literatur/mockobjects.pdf
421
- * Mock Roles, Not Objects: http://jmock.org/oopsla2004.pdf
420
+ * Endo-Testing: http://www.ccs.neu.edu/research/demeter/related-work/extreme-programming/MockObjectsFinal.PDF
421
+ * Mock Roles, Not Objects: http://www.jmock.org/oopsla2004.pdf
422
422
  * Test Double: http://www.martinfowler.com/bliki/TestDouble.html
423
423
  * Test Double Patterns: http://xunitpatterns.com/Test%20Double%20Patterns.html
424
424
  * Mocks aren't stubs: http://www.martinfowler.com/articles/mocksArentStubs.html
@@ -1,5 +1,6 @@
1
1
  %w[
2
2
  any_instance/chain
3
+ any_instance/error_generator
3
4
  any_instance/stub_chain
4
5
  any_instance/stub_chain_chain
5
6
  any_instance/expect_chain_chain
@@ -76,7 +76,7 @@ module RSpec
76
76
  end
77
77
 
78
78
  def never
79
- ErrorGenerator.raise_double_negation_error("expect_any_instance_of(MyClass)") if negated?
79
+ AnyInstance.error_generator.raise_double_negation_error("expect_any_instance_of(MyClass)") if negated?
80
80
  super
81
81
  end
82
82
 
@@ -0,0 +1,31 @@
1
+ module RSpec
2
+ module Mocks
3
+ module AnyInstance
4
+ # @private
5
+ class ErrorGenerator < ::RSpec::Mocks::ErrorGenerator
6
+ def raise_second_instance_received_message_error(unfulfilled_expectations)
7
+ __raise "Exactly one instance should have received the following " \
8
+ "message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}"
9
+ end
10
+
11
+ def raise_does_not_implement_error(klass, method_name)
12
+ __raise "#{klass} does not implement ##{method_name}"
13
+ end
14
+
15
+ def raise_message_already_received_by_other_instance_error(method_name, object_inspect, invoked_instance)
16
+ __raise "The message '#{method_name}' was received by #{object_inspect} " \
17
+ "but has already been received by #{invoked_instance}"
18
+ end
19
+
20
+ def raise_not_supported_with_prepend_error(method_name, problem_mod)
21
+ __raise "Using `any_instance` to stub a method (#{method_name}) that has been " \
22
+ "defined on a prepended module (#{problem_mod}) is not supported."
23
+ end
24
+ end
25
+
26
+ def self.error_generator
27
+ @error_generator ||= ErrorGenerator.new
28
+ end
29
+ end
30
+ end
31
+ end
@@ -75,9 +75,7 @@ module RSpec
75
75
  return unless ExpectationChain === instance
76
76
  return if @instance_with_expectation.equal?(instance)
77
77
 
78
- raise RSpec::Mocks::MockExpectationError,
79
- "Exactly one instance should have received the following " \
80
- "message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}"
78
+ AnyInstance.error_generator.raise_second_instance_received_message_error(unfulfilled_expectations)
81
79
  end
82
80
  end
83
81
  end
@@ -76,7 +76,7 @@ module RSpec
76
76
  # @see Methods#unstub
77
77
  def unstub(method_name)
78
78
  unless @observed_methods.include?(method_name.to_sym)
79
- raise RSpec::Mocks::MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
79
+ AnyInstance.error_generator.raise_method_not_stubbed_error(method_name)
80
80
  end
81
81
  message_chains.remove_stub_chains_for!(method_name)
82
82
  stubs[method_name].clear
@@ -91,9 +91,7 @@ module RSpec
91
91
  return unless @expectation_set
92
92
  return if message_chains.all_expectations_fulfilled?
93
93
 
94
- raise RSpec::Mocks::MockExpectationError,
95
- "Exactly one instance should have received the following " \
96
- "message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}"
94
+ AnyInstance.error_generator.raise_second_instance_received_message_error(message_chains.unfulfilled_expectations)
97
95
  end
98
96
 
99
97
  # @private
@@ -221,8 +219,7 @@ module RSpec
221
219
 
222
220
  if RSpec::Mocks.configuration.verify_partial_doubles?
223
221
  unless public_protected_or_private_method_defined?(method_name)
224
- raise MockExpectationError,
225
- "#{@klass} does not implement ##{method_name}"
222
+ AnyInstance.error_generator.raise_does_not_implement_error(@klass, method_name)
226
223
  end
227
224
  end
228
225
 
@@ -242,7 +239,9 @@ module RSpec
242
239
  @klass.__send__(:define_method, method_name) do |*_args, &_blk|
243
240
  invoked_instance = recorder.instance_that_received(method_name)
244
241
  inspect = "#<#{self.class}:#{object_id} #{instance_variables.map { |name| "#{name}=#{instance_variable_get name}" }.join(', ')}>"
245
- raise RSpec::Mocks::MockExpectationError, "The message '#{method_name}' was received by #{inspect} but has already been received by #{invoked_instance}"
242
+ AnyInstance.error_generator.raise_message_already_received_by_other_instance_error(
243
+ method_name, inspect, invoked_instance
244
+ )
246
245
  end
247
246
  end
248
247
 
@@ -252,9 +251,7 @@ module RSpec
252
251
  problem_mod = prepended_modules.find { |mod| mod.method_defined?(method_name) }
253
252
  return unless problem_mod
254
253
 
255
- raise RSpec::Mocks::MockExpectationError,
256
- "Using `any_instance` to stub a method (#{method_name}) that has been " \
257
- "defined on a prepended module (#{problem_mod}) is not supported."
254
+ AnyInstance.error_generator.raise_not_supported_with_prepend_error(method_name, problem_mod)
258
255
  end
259
256
  else
260
257
  def allow_no_prepended_module_definition_of(_method_name)
@@ -64,7 +64,7 @@ module RSpec
64
64
  def resolve_expected_args_based_on(actual_args)
65
65
  return [] if [ArgumentMatchers::NoArgsMatcher::INSTANCE] == expected_args
66
66
 
67
- any_args_index = expected_args.index(ArgumentMatchers::AnyArgsMatcher::INSTANCE)
67
+ any_args_index = expected_args.index { |a| ArgumentMatchers::AnyArgsMatcher::INSTANCE == a }
68
68
  return expected_args unless any_args_index
69
69
 
70
70
  replace_any_args_with_splat_of_anything(any_args_index, actual_args.count)
@@ -81,10 +81,10 @@ module RSpec
81
81
  end
82
82
 
83
83
  def ensure_expected_args_valid!
84
- if expected_args.count(ArgumentMatchers::AnyArgsMatcher::INSTANCE) > 1
84
+ if expected_args.count { |a| ArgumentMatchers::AnyArgsMatcher::INSTANCE == a } > 1
85
85
  raise ArgumentError, "`any_args` can only be passed to " \
86
86
  "`with` once but you have passed it multiple times."
87
- elsif expected_args.count > 1 && expected_args.include?(ArgumentMatchers::NoArgsMatcher::INSTANCE)
87
+ elsif expected_args.count > 1 && expected_args.any? { |a| ArgumentMatchers::NoArgsMatcher::INSTANCE == a }
88
88
  raise ArgumentError, "`no_args` can only be passed as a " \
89
89
  "singleton argument to `with` (i.e. `with(no_args)`), " \
90
90
  "but you have passed additional arguments."
@@ -103,17 +103,18 @@ module RSpec
103
103
  # Provides a way to perform customisations when verifying doubles.
104
104
  #
105
105
  # @example
106
- # RSpec::Mocks.configuration.when_declaring_verifying_double do |ref|
106
+ # RSpec::Mocks.configuration.before_verifying_doubles do |ref|
107
107
  # ref.some_method!
108
108
  # end
109
- def when_declaring_verifying_double(&block)
110
- verifying_double_declaration_callbacks << block
109
+ def before_verifying_doubles(&block)
110
+ verifying_double_callbacks << block
111
111
  end
112
+ alias :when_declaring_verifying_double :before_verifying_doubles
112
113
 
113
114
  # @api private
114
115
  # Returns an array of blocks to call when verifying doubles
115
- def verifying_double_declaration_callbacks
116
- @verifying_double_declaration_callbacks ||= []
116
+ def verifying_double_callbacks
117
+ @verifying_double_callbacks ||= []
117
118
  end
118
119
 
119
120
  def transfer_nested_constants?
@@ -1,4 +1,4 @@
1
- RSpec::Support.require_rspec_support 'differ'
1
+ RSpec::Support.require_rspec_support "object_formatter"
2
2
 
3
3
  module RSpec
4
4
  module Mocks
@@ -36,9 +36,8 @@ module RSpec
36
36
  class ErrorGenerator
37
37
  attr_writer :opts
38
38
 
39
- def initialize(target, name)
39
+ def initialize(target=nil)
40
40
  @target = target
41
- @name = name
42
41
  end
43
42
 
44
43
  # @private
@@ -47,41 +46,26 @@ module RSpec
47
46
  end
48
47
 
49
48
  # @private
50
- def raise_unexpected_message_error(message, *args)
51
- __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)}"
52
51
  end
53
52
 
54
53
  # @private
55
- def raise_unexpected_message_args_error(expectation, *args)
56
- expected_args = format_args(*expectation.expected_args)
57
- actual_args = format_received_args(*args)
58
- diff = diff_message(expectation.expected_args, args)
59
-
60
- message = default_error_message(expectation, expected_args, actual_args)
61
- message << "\nDiff:#{diff}" unless diff.empty?
62
-
63
- __raise message
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
64
56
  end
65
57
 
66
58
  # @private
67
- def raise_missing_default_stub_error(expectation, *args)
68
- expected_args = format_args(*expectation.expected_args)
69
- actual_args = format_received_args(*args)
70
- diff = diff_message(expectation.expected_args, args)
71
-
72
- message = default_error_message(expectation, expected_args, actual_args)
73
- message << "\nDiff:\n #{diff}" unless diff.empty?
59
+ def raise_missing_default_stub_error(expectation, args_for_multiple_calls)
60
+ message = error_message(expectation, args_for_multiple_calls)
74
61
  message << "\n Please stub a default value first if message might be received with other args as well. \n"
75
62
 
76
63
  __raise message
77
64
  end
78
65
 
79
66
  # @private
80
- def raise_similar_message_args_error(expectation, *args_for_multiple_calls)
81
- expected_args = format_args(*expectation.expected_args)
82
- actual_args = args_for_multiple_calls.map { |a| format_received_args(*a) }.join(", ")
83
-
84
- __raise(default_error_message(expectation, expected_args, 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
85
69
  end
86
70
 
87
71
  def default_error_message(expectation, expected_args, actual_args)
@@ -95,19 +79,37 @@ module RSpec
95
79
 
96
80
  # rubocop:disable Style/ParameterLists
97
81
  # @private
98
- def raise_expectation_error(message, expected_received_count, argument_list_matcher, actual_received_count, expectation_count_type, *args)
82
+ def raise_expectation_error(message, expected_received_count, argument_list_matcher,
83
+ actual_received_count, expectation_count_type, args,
84
+ backtrace_line=nil, source_id=nil)
99
85
  expected_part = expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher)
100
- received_part = received_part_of_expectation_error(actual_received_count, *args)
101
- __raise "(#{intro}).#{message}#{format_args(*args)}\n #{expected_part}\n #{received_part}"
86
+ received_part = received_part_of_expectation_error(actual_received_count, args)
87
+ __raise "(#{intro(:unwrapped)}).#{message}#{format_args(args)}\n #{expected_part}\n #{received_part}", backtrace_line, source_id
102
88
  end
103
89
  # rubocop:enable Style/ParameterLists
104
90
 
105
91
  # @private
106
- def raise_unimplemented_error(doubled_module, method_name)
107
- __raise "%s does not implement: %s" % [
108
- doubled_module.description,
109
- method_name
110
- ]
92
+ def raise_unimplemented_error(doubled_module, method_name, object)
93
+ message = case object
94
+ when InstanceVerifyingDouble
95
+ "the %s class does not implement the instance method: %s" <<
96
+ if ObjectMethodReference.for(doubled_module, method_name).implemented?
97
+ ". Perhaps you meant to use `class_double` instead?"
98
+ else
99
+ ""
100
+ end
101
+ when ClassVerifyingDouble
102
+ "the %s class does not implement the class method: %s" <<
103
+ if InstanceMethodReference.for(doubled_module, method_name).implemented?
104
+ ". Perhaps you meant to use `instance_double` instead?"
105
+ else
106
+ ""
107
+ end
108
+ else
109
+ "%s does not implement: %s"
110
+ end
111
+
112
+ __raise message % [doubled_module.description, method_name]
111
113
  end
112
114
 
113
115
  # @private
@@ -132,101 +134,131 @@ module RSpec
132
134
  end
133
135
 
134
136
  # @private
135
- def received_part_of_expectation_error(actual_received_count, *args)
136
- "received: #{count_message(actual_received_count)}" +
137
- actual_method_call_args_description(actual_received_count, args)
137
+ def describe_expectation(verb, message, expected_received_count, _actual_received_count, args)
138
+ "#{verb} #{message}#{format_args(args)} #{count_message(expected_received_count)}"
138
139
  end
139
140
 
140
141
  # @private
141
- def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher)
142
- "expected: #{count_message(expected_received_count, expectation_count_type)}" +
143
- expected_method_call_args_description(argument_list_matcher.expected_args)
142
+ def raise_out_of_order_error(message)
143
+ __raise "#{intro} received :#{message} out of order"
144
144
  end
145
145
 
146
146
  # @private
147
- def actual_method_call_args_description(count, args)
148
- method_call_args_description(args) ||
149
- if count > 0 && args.length > 0
150
- " with arguments: #{args.inspect.gsub(/\A\[(.+)\]\z/, '(\1)')}"
151
- else
152
- ""
153
- end
147
+ def raise_missing_block_error(args_to_yield)
148
+ __raise "#{intro} asked to yield |#{arg_list(args_to_yield)}| but no block was passed"
154
149
  end
155
150
 
156
151
  # @private
157
- def expected_method_call_args_description(args)
158
- method_call_args_description(args) ||
159
- if args.length > 0
160
- " with arguments: #{format_args(*args)}"
161
- else
162
- ""
163
- end
152
+ def raise_wrong_arity_error(args_to_yield, signature)
153
+ __raise "#{intro} yielded |#{arg_list(args_to_yield)}| to block with #{signature.description}"
164
154
  end
165
155
 
166
156
  # @private
167
- def method_call_args_description(args)
168
- case args.first
169
- when ArgumentMatchers::AnyArgsMatcher then " with any arguments"
170
- when ArgumentMatchers::NoArgsMatcher then " with no arguments"
171
- end
157
+ def raise_only_valid_on_a_partial_double(method)
158
+ __raise "#{intro} is a pure test double. `#{method}` is only " \
159
+ "available on a partial double."
172
160
  end
173
161
 
174
162
  # @private
175
- def describe_expectation(verb, message, expected_received_count, _actual_received_count, *args)
176
- "#{verb} #{message}#{format_args(*args)} #{count_message(expected_received_count)}"
163
+ def raise_expectation_on_unstubbed_method(method)
164
+ __raise "#{intro} expected to have received #{method}, but that " \
165
+ "object is not a spy or method has not been stubbed."
177
166
  end
178
167
 
179
168
  # @private
180
- def raise_out_of_order_error(message)
181
- __raise "#{intro} received :#{message} out of order"
169
+ def raise_expectation_on_mocked_method(method)
170
+ __raise "#{intro} expected to have received #{method}, but that " \
171
+ "method has been mocked instead of stubbed or spied."
182
172
  end
183
173
 
184
174
  # @private
185
- def raise_block_failed_error(message, detail)
186
- __raise "#{intro} received :#{message} but passed block failed with: #{detail}"
175
+ def raise_double_negation_error(wrapped_expression)
176
+ __raise "Isn't life confusing enough? You've already set a " \
177
+ "negative message expectation and now you are trying to " \
178
+ "negate it again with `never`. What does an expression like " \
179
+ "`#{wrapped_expression}.not_to receive(:msg).never` even mean?"
187
180
  end
188
181
 
189
182
  # @private
190
- def raise_missing_block_error(args_to_yield)
191
- __raise "#{intro} asked to yield |#{arg_list(*args_to_yield)}| but no block was passed"
183
+ def raise_verifying_double_not_defined_error(ref)
184
+ notify(VerifyingDoubleNotDefinedError.new(
185
+ "#{ref.description.inspect} is not a defined constant. " \
186
+ "Perhaps you misspelt it? " \
187
+ "Disable check with `verify_doubled_constant_names` configuration option."
188
+ ))
192
189
  end
193
190
 
194
191
  # @private
195
- def raise_wrong_arity_error(args_to_yield, signature)
196
- __raise "#{intro} yielded |#{arg_list(*args_to_yield)}| to block with #{signature.description}"
192
+ def raise_have_received_disallowed(type, reason)
193
+ __raise "Using #{type}(...) with the `have_received` " \
194
+ "matcher is not supported#{reason}."
197
195
  end
198
196
 
199
197
  # @private
200
- def raise_only_valid_on_a_partial_double(method)
201
- __raise "#{intro} is a pure test double. `#{method}` is only " \
202
- "available on a partial double."
198
+ def raise_cant_constrain_count_for_negated_have_received_error(count_constraint)
199
+ __raise "can't use #{count_constraint} when negative"
203
200
  end
204
201
 
205
202
  # @private
206
- def raise_expectation_on_unstubbed_method(method)
207
- __raise "#{intro} expected to have received #{method}, but that " \
208
- "object is not a spy or method has not been stubbed."
203
+ def raise_method_not_stubbed_error(method_name)
204
+ __raise "The method `#{method_name}` was not stubbed or was already unstubbed"
209
205
  end
210
206
 
211
207
  # @private
212
- def raise_expectation_on_mocked_method(method)
213
- __raise "#{intro} expected to have received #{method}, but that " \
214
- "method has been mocked instead of stubbed or spied."
215
- end
208
+ def raise_already_invoked_error(message, calling_customization)
209
+ error_message = "The message expectation for #{intro}.#{message} has already been invoked " \
210
+ "and cannot be modified further (e.g. using `#{calling_customization}`). All message expectation " \
211
+ "customizations must be applied before it is used for the first time."
216
212
 
217
- def self.raise_double_negation_error(wrapped_expression)
218
- raise "Isn't life confusing enough? You've already set a " \
219
- "negative message expectation and now you are trying to " \
220
- "negate it again with `never`. What does an expression like " \
221
- "`#{wrapped_expression}.not_to receive(:msg).never` even mean?"
213
+ notify MockExpectationAlreadyInvokedError.new(error_message)
222
214
  end
223
215
 
224
216
  private
225
217
 
218
+ def received_part_of_expectation_error(actual_received_count, args)
219
+ "received: #{count_message(actual_received_count)}" +
220
+ method_call_args_description(args) do
221
+ actual_received_count > 0 && args.length > 0
222
+ end
223
+ end
224
+
225
+ def expected_part_of_expectation_error(expected_received_count, expectation_count_type, argument_list_matcher)
226
+ "expected: #{count_message(expected_received_count, expectation_count_type)}" +
227
+ method_call_args_description(argument_list_matcher.expected_args) do
228
+ argument_list_matcher.expected_args.length > 0
229
+ end
230
+ end
231
+
232
+ def method_call_args_description(args)
233
+ case args.first
234
+ when ArgumentMatchers::AnyArgsMatcher then " with any arguments"
235
+ when ArgumentMatchers::NoArgsMatcher then " with no arguments"
236
+ else
237
+ if yield
238
+ " with arguments: #{format_args(args)}"
239
+ else
240
+ ""
241
+ end
242
+ end
243
+ end
244
+
226
245
  def unexpected_arguments_message(expected_args_string, actual_args_string)
227
246
  "with unexpected arguments\n expected: #{expected_args_string}\n got: #{actual_args_string}"
228
247
  end
229
248
 
249
+ def error_message(expectation, args_for_multiple_calls)
250
+ expected_args = format_args(expectation.expected_args)
251
+ actual_args = format_received_args(args_for_multiple_calls)
252
+ message = default_error_message(expectation, expected_args, actual_args)
253
+
254
+ if args_for_multiple_calls.one?
255
+ diff = diff_message(expectation.expected_args, args_for_multiple_calls.first)
256
+ message << "\nDiff:#{diff}" unless diff.strip.empty?
257
+ end
258
+
259
+ message
260
+ end
261
+
230
262
  def diff_message(expected_args, actual_args)
231
263
  formatted_expected_args = expected_args.map do |x|
232
264
  RSpec::Support.rspec_description_for_object(x)
@@ -253,47 +285,54 @@ module RSpec
253
285
  RSpec::Support::Differ.new(:color => RSpec::Mocks.configuration.color?)
254
286
  end
255
287
 
256
- def intro
257
- if @name
258
- "Double #{@name.inspect}"
259
- elsif TestDouble === @target
260
- "Double"
261
- elsif Class === @target
262
- "<#{@target.inspect} (class)>"
263
- elsif @target
264
- @target
265
- else
266
- "nil"
288
+ def intro(unwrapped=false)
289
+ case @target
290
+ when TestDouble then TestDoubleFormatter.format(@target, unwrapped)
291
+ when Class then
292
+ formatted = "#{@target.inspect} (class)"
293
+ return formatted if unwrapped
294
+ "#<#{formatted}>"
295
+ when NilClass then "nil"
296
+ else @target
267
297
  end
268
298
  end
269
299
 
270
- def __raise(message)
300
+ def __raise(message, backtrace_line=nil, source_id=nil)
271
301
  message = opts[:message] unless opts[:message].nil?
272
- Kernel.raise(RSpec::Mocks::MockExpectationError, message)
302
+ exception = RSpec::Mocks::MockExpectationError.new(message)
303
+ prepend_to_backtrace(exception, backtrace_line) if backtrace_line
304
+ notify exception, :source_id => source_id
273
305
  end
274
306
 
275
- def arg_message(*args)
276
- " with " + format_args(*args)
277
- end
278
-
279
- def format_args(*args)
280
- args.empty? ? "(no args)" : "(" + arg_list(*args) + ")"
307
+ if RSpec::Support::Ruby.jruby?
308
+ def prepend_to_backtrace(exception, line)
309
+ raise exception
310
+ rescue RSpec::Mocks::MockExpectationError => with_backtrace
311
+ with_backtrace.backtrace.unshift(line)
312
+ end
313
+ else
314
+ def prepend_to_backtrace(exception, line)
315
+ exception.set_backtrace(caller.unshift line)
316
+ end
281
317
  end
282
318
 
283
- def arg_list(*args)
284
- args.map { |arg| arg_has_valid_description?(arg) ? arg.description : arg.inspect }.join(", ")
319
+ def notify(*args)
320
+ RSpec::Support.notify_failure(*args)
285
321
  end
286
322
 
287
- def arg_has_valid_description?(arg)
288
- RSpec::Support.is_a_matcher?(arg) && arg.respond_to?(:description)
323
+ def format_args(args)
324
+ return "(no args)" if args.empty?
325
+ "(#{arg_list(args)})"
289
326
  end
290
327
 
291
- def format_received_args(*args)
292
- args.empty? ? "(no args)" : "(" + received_arg_list(*args) + ")"
328
+ def arg_list(args)
329
+ args.map { |arg| RSpec::Support::ObjectFormatter.format(arg) }.join(", ")
293
330
  end
294
331
 
295
- def received_arg_list(*args)
296
- args.map(&:inspect).join(", ")
332
+ def format_received_args(args_for_multiple_calls)
333
+ grouped_args(args_for_multiple_calls).map do |args_for_one_call, index|
334
+ "#{format_args(args_for_one_call)}#{group_count(index, args_for_multiple_calls)}"
335
+ end.join("\n ")
297
336
  end
298
337
 
299
338
  def count_message(count, expectation_count_type=nil)
@@ -305,6 +344,19 @@ module RSpec
305
344
  def times(count)
306
345
  "#{count} time#{count == 1 ? '' : 's'}"
307
346
  end
347
+
348
+ def grouped_args(args)
349
+ Hash[args.group_by { |x| x }.map { |k, v| [k, v.count] }]
350
+ end
351
+
352
+ def group_count(index, args)
353
+ " (#{times(index)})" if args.size > 1 || index > 1
354
+ end
355
+ end
356
+
357
+ # @private
358
+ def self.error_generator
359
+ @error_generator ||= ErrorGenerator.new
308
360
  end
309
361
  end
310
362
  end