rspec-mocks 3.2.1 → 3.3.0

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