rspec-expectations 3.0.0.beta1 → 3.0.0.beta2

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 (122) hide show
  1. data.tar.gz.sig +2 -2
  2. data/.yardopts +1 -0
  3. data/Changelog.md +138 -0
  4. data/README.md +75 -8
  5. data/features/README.md +2 -2
  6. data/features/built_in_matchers/README.md +12 -9
  7. data/features/built_in_matchers/comparisons.feature +2 -2
  8. data/features/built_in_matchers/contain_exactly.feature +46 -0
  9. data/features/built_in_matchers/expect_change.feature +2 -2
  10. data/features/built_in_matchers/include.feature +0 -48
  11. data/features/built_in_matchers/output.feature +70 -0
  12. data/features/composing_matchers.feature +250 -0
  13. data/features/compound_expectations.feature +45 -0
  14. data/features/custom_matchers/access_running_example.feature +1 -1
  15. data/features/custom_matchers/define_matcher.feature +6 -6
  16. data/features/custom_matchers/define_matcher_outside_rspec.feature +4 -8
  17. data/features/test_frameworks/{test_unit.feature → minitest.feature} +11 -11
  18. data/lib/rspec/expectations.rb +31 -42
  19. data/lib/rspec/expectations/diff_presenter.rb +141 -0
  20. data/lib/rspec/expectations/differ.rb +22 -132
  21. data/lib/rspec/expectations/encoded_string.rb +56 -0
  22. data/lib/rspec/expectations/expectation_target.rb +0 -30
  23. data/lib/rspec/expectations/fail_with.rb +2 -2
  24. data/lib/rspec/expectations/handler.rb +128 -31
  25. data/lib/rspec/expectations/minitest_integration.rb +16 -0
  26. data/lib/rspec/expectations/syntax.rb +4 -58
  27. data/lib/rspec/expectations/version.rb +1 -1
  28. data/lib/rspec/matchers.rb +298 -60
  29. data/lib/rspec/matchers/aliased_matcher.rb +35 -0
  30. data/lib/rspec/matchers/built_in.rb +37 -33
  31. data/lib/rspec/matchers/built_in/base_matcher.rb +25 -15
  32. data/lib/rspec/matchers/built_in/be.rb +23 -31
  33. data/lib/rspec/matchers/built_in/be_between.rb +55 -0
  34. data/lib/rspec/matchers/built_in/be_within.rb +15 -11
  35. data/lib/rspec/matchers/built_in/change.rb +198 -81
  36. data/lib/rspec/matchers/built_in/compound.rb +106 -0
  37. data/lib/rspec/matchers/built_in/contain_exactly.rb +245 -0
  38. data/lib/rspec/matchers/built_in/eq.rb +43 -4
  39. data/lib/rspec/matchers/built_in/eql.rb +2 -2
  40. data/lib/rspec/matchers/built_in/equal.rb +35 -18
  41. data/lib/rspec/matchers/built_in/has.rb +16 -15
  42. data/lib/rspec/matchers/built_in/include.rb +45 -23
  43. data/lib/rspec/matchers/built_in/match.rb +6 -3
  44. data/lib/rspec/matchers/built_in/operators.rb +103 -0
  45. data/lib/rspec/matchers/built_in/output.rb +108 -0
  46. data/lib/rspec/matchers/built_in/raise_error.rb +9 -15
  47. data/lib/rspec/matchers/built_in/respond_to.rb +5 -4
  48. data/lib/rspec/matchers/built_in/satisfy.rb +4 -3
  49. data/lib/rspec/matchers/built_in/start_and_end_with.rb +37 -16
  50. data/lib/rspec/matchers/built_in/throw_symbol.rb +6 -5
  51. data/lib/rspec/matchers/built_in/yield.rb +31 -29
  52. data/lib/rspec/matchers/composable.rb +138 -0
  53. data/lib/rspec/matchers/dsl.rb +330 -0
  54. data/lib/rspec/matchers/generated_descriptions.rb +6 -6
  55. data/lib/rspec/matchers/matcher_delegator.rb +33 -0
  56. data/lib/rspec/matchers/pretty.rb +13 -2
  57. data/spec/rspec/expectations/{differ_spec.rb → diff_presenter_spec.rb} +56 -36
  58. data/spec/rspec/expectations/encoded_string_spec.rb +74 -0
  59. data/spec/rspec/expectations/extensions/kernel_spec.rb +11 -11
  60. data/spec/rspec/expectations/fail_with_spec.rb +8 -8
  61. data/spec/rspec/expectations/handler_spec.rb +27 -49
  62. data/spec/rspec/expectations/minitest_integration_spec.rb +27 -0
  63. data/spec/rspec/expectations/syntax_spec.rb +17 -67
  64. data/spec/rspec/expectations_spec.rb +7 -52
  65. data/spec/rspec/matchers/aliased_matcher_spec.rb +48 -0
  66. data/spec/rspec/matchers/aliases_spec.rb +449 -0
  67. data/spec/rspec/matchers/{base_matcher_spec.rb → built_in/base_matcher_spec.rb} +24 -3
  68. data/spec/rspec/matchers/built_in/be_between_spec.rb +159 -0
  69. data/spec/rspec/matchers/{be_instance_of_spec.rb → built_in/be_instance_of_spec.rb} +0 -0
  70. data/spec/rspec/matchers/{be_kind_of_spec.rb → built_in/be_kind_of_spec.rb} +0 -0
  71. data/spec/rspec/matchers/{be_spec.rb → built_in/be_spec.rb} +76 -32
  72. data/spec/rspec/matchers/{be_within_spec.rb → built_in/be_within_spec.rb} +6 -2
  73. data/spec/rspec/matchers/{change_spec.rb → built_in/change_spec.rb} +310 -69
  74. data/spec/rspec/matchers/built_in/compound_spec.rb +292 -0
  75. data/spec/rspec/matchers/built_in/contain_exactly_spec.rb +441 -0
  76. data/spec/rspec/matchers/{cover_spec.rb → built_in/cover_spec.rb} +0 -0
  77. data/spec/rspec/matchers/built_in/eq_spec.rb +156 -0
  78. data/spec/rspec/matchers/{eql_spec.rb → built_in/eql_spec.rb} +2 -2
  79. data/spec/rspec/matchers/built_in/equal_spec.rb +106 -0
  80. data/spec/rspec/matchers/{exist_spec.rb → built_in/exist_spec.rb} +1 -1
  81. data/spec/rspec/matchers/{has_spec.rb → built_in/has_spec.rb} +39 -0
  82. data/spec/rspec/matchers/{include_spec.rb → built_in/include_spec.rb} +118 -109
  83. data/spec/rspec/matchers/{match_spec.rb → built_in/match_spec.rb} +30 -2
  84. data/spec/rspec/matchers/{operator_matcher_spec.rb → built_in/operators_spec.rb} +26 -26
  85. data/spec/rspec/matchers/built_in/output_spec.rb +165 -0
  86. data/spec/rspec/matchers/{raise_error_spec.rb → built_in/raise_error_spec.rb} +81 -11
  87. data/spec/rspec/matchers/{respond_to_spec.rb → built_in/respond_to_spec.rb} +0 -0
  88. data/spec/rspec/matchers/{satisfy_spec.rb → built_in/satisfy_spec.rb} +0 -0
  89. data/spec/rspec/matchers/{start_with_end_with_spec.rb → built_in/start_and_end_with_spec.rb} +82 -15
  90. data/spec/rspec/matchers/{throw_symbol_spec.rb → built_in/throw_symbol_spec.rb} +29 -10
  91. data/spec/rspec/matchers/{yield_spec.rb → built_in/yield_spec.rb} +90 -0
  92. data/spec/rspec/matchers/configuration_spec.rb +7 -39
  93. data/spec/rspec/matchers/description_generation_spec.rb +22 -6
  94. data/spec/rspec/matchers/dsl_spec.rb +838 -0
  95. data/spec/rspec/matchers/legacy_spec.rb +101 -0
  96. data/spec/rspec/matchers_spec.rb +74 -0
  97. data/spec/spec_helper.rb +35 -21
  98. data/spec/support/shared_examples.rb +26 -4
  99. metadata +172 -116
  100. metadata.gz.sig +3 -4
  101. checksums.yaml +0 -15
  102. checksums.yaml.gz.sig +0 -0
  103. data/features/built_in_matchers/match_array.feature +0 -37
  104. data/lib/rspec/expectations/errors.rb +0 -9
  105. data/lib/rspec/expectations/extensions.rb +0 -1
  106. data/lib/rspec/expectations/extensions/object.rb +0 -29
  107. data/lib/rspec/matchers/built_in/match_array.rb +0 -51
  108. data/lib/rspec/matchers/compatibility.rb +0 -14
  109. data/lib/rspec/matchers/matcher.rb +0 -301
  110. data/lib/rspec/matchers/method_missing.rb +0 -12
  111. data/lib/rspec/matchers/operator_matcher.rb +0 -99
  112. data/lib/rspec/matchers/test_unit_integration.rb +0 -11
  113. data/spec/rspec/matchers/eq_spec.rb +0 -60
  114. data/spec/rspec/matchers/equal_spec.rb +0 -78
  115. data/spec/rspec/matchers/include_matcher_integration_spec.rb +0 -30
  116. data/spec/rspec/matchers/match_array_spec.rb +0 -194
  117. data/spec/rspec/matchers/matcher_spec.rb +0 -706
  118. data/spec/rspec/matchers/matchers_spec.rb +0 -36
  119. data/spec/rspec/matchers/method_missing_spec.rb +0 -28
  120. data/spec/support/classes.rb +0 -56
  121. data/spec/support/in_sub_process.rb +0 -37
  122. data/spec/support/ruby_version.rb +0 -10
@@ -0,0 +1,106 @@
1
+ module RSpec
2
+ module Matchers
3
+ module BuiltIn
4
+ # Base class for `and` and `or` compound matchers.
5
+ # @api private
6
+ class Compound < BaseMatcher
7
+ attr_reader :matcher_1, :matcher_2
8
+
9
+ def initialize(matcher_1, matcher_2)
10
+ @matcher_1 = matcher_1
11
+ @matcher_2 = matcher_2
12
+ end
13
+
14
+ def does_not_match?(actual)
15
+ raise NotImplementedError,
16
+ "`expect(...).not_to matcher.#{conjunction} matcher` is not supported"
17
+ end
18
+
19
+ def description
20
+ singleline_message(matcher_1.description, matcher_2.description)
21
+ end
22
+
23
+ private
24
+
25
+ def indent_multiline_message(message)
26
+ message.lines.map do |line|
27
+ line =~ /\S/ ? ' ' + line : line
28
+ end.join
29
+ end
30
+
31
+ def compound_failure_message
32
+ message_1 = matcher_1.failure_message
33
+ message_2 = matcher_2.failure_message
34
+
35
+ if multiline?(message_1) || multiline?(message_2)
36
+ multiline_message(message_1, message_2)
37
+ else
38
+ singleline_message(message_1, message_2)
39
+ end
40
+ end
41
+
42
+ def multiline_message(message_1, message_2)
43
+ [
44
+ indent_multiline_message(message_1.sub(/\n+\z/, '')),
45
+ "...#{conjunction}:",
46
+ indent_multiline_message(message_2.sub(/\A\n+/, ''))
47
+ ].join("\n\n")
48
+ end
49
+
50
+ def multiline?(message)
51
+ message.lines.count > 1
52
+ end
53
+
54
+ def singleline_message(message_1, message_2)
55
+ [message_1, conjunction, message_2].join(' ')
56
+ end
57
+
58
+ # Matcher used to represent a compound `and` expectation.
59
+ # @api public
60
+ class And < self
61
+ def failure_message
62
+ if @matcher_1_matches
63
+ matcher_2.failure_message
64
+ elsif @matcher_2_matches
65
+ matcher_1.failure_message
66
+ else
67
+ compound_failure_message
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def match(expected, actual)
74
+ @matcher_1_matches = matcher_1.matches?(actual)
75
+ @matcher_2_matches = matcher_2.matches?(actual)
76
+
77
+ @matcher_1_matches && @matcher_2_matches
78
+ end
79
+
80
+ def conjunction
81
+ "and"
82
+ end
83
+ end
84
+
85
+ # Matcher used to represent a compound `or` expectation.
86
+ # @api public
87
+ class Or < self
88
+ def failure_message
89
+ compound_failure_message
90
+ end
91
+
92
+ private
93
+
94
+ def match(expected, actual)
95
+ matcher_1.matches?(actual) || matcher_2.matches?(actual)
96
+ end
97
+
98
+ def conjunction
99
+ "or"
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+
@@ -0,0 +1,245 @@
1
+ module RSpec
2
+ module Matchers
3
+ module BuiltIn
4
+ class ContainExactly < BaseMatcher
5
+ def match(expected, actual)
6
+ convert_actual_to_an_array or return false
7
+ match_when_sorted? || (extra_items.empty? && missing_items.empty?)
8
+ end
9
+
10
+ def failure_message
11
+ if Array === actual
12
+ message = "expected collection contained: #{safe_sort(surface_descriptions_in expected).inspect}\n"
13
+ message += "actual collection contained: #{safe_sort(actual).inspect}\n"
14
+ message += "the missing elements were: #{safe_sort(surface_descriptions_in missing_items).inspect}\n" unless missing_items.empty?
15
+ message += "the extra elements were: #{safe_sort(extra_items).inspect}\n" unless extra_items.empty?
16
+ message
17
+ else
18
+ "expected a collection that can be converted to an array with " +
19
+ "`#to_ary` or `#to_a`, but got #{actual.inspect}"
20
+ end
21
+ end
22
+
23
+ def failure_message_when_negated
24
+ "`contain_exactly` does not support negation"
25
+ end
26
+
27
+ def description
28
+ "contain exactly #{_pretty_print(surface_descriptions_in expected)}"
29
+ end
30
+
31
+ private
32
+
33
+ # This cannot always work (e.g. when dealing with unsortable items,
34
+ # or matchers as expected items), but it's practically free compared to
35
+ # the slowness of the full matching algorithm, and in common cases this
36
+ # works, so it's worth a try.
37
+ def match_when_sorted?
38
+ values_match?(safe_sort(expected), safe_sort(actual))
39
+ end
40
+
41
+ def convert_actual_to_an_array
42
+ if actual.respond_to?(:to_ary)
43
+ @actual = actual.to_ary
44
+ elsif enumerable?(actual) && actual.respond_to?(:to_a)
45
+ @actual = actual.to_a
46
+ else
47
+ return false
48
+ end
49
+ end
50
+
51
+ def safe_sort(array)
52
+ array.sort rescue array
53
+ end
54
+
55
+ def missing_items
56
+ @missing_items ||= best_solution.unmatched_expected_indexes.map do |index|
57
+ expected[index]
58
+ end
59
+ end
60
+
61
+ def extra_items
62
+ @extra_items ||= best_solution.unmatched_actual_indexes.map do |index|
63
+ actual[index]
64
+ end
65
+ end
66
+
67
+ def best_solution
68
+ @best_solution ||= pairings_maximizer.find_best_solution
69
+ end
70
+
71
+ def pairings_maximizer
72
+ @pairings_maximizer ||= begin
73
+ expected_matches = {}
74
+ actual_matches = {}
75
+
76
+ expected.each_with_index do |e, ei|
77
+ expected_matches[ei] ||= []
78
+
79
+ actual.each_with_index do |a, ai|
80
+ actual_matches[ai] ||= []
81
+
82
+ # Normally we'd call `values_match?(e, a)` here but that contains
83
+ # some extra checks we don't need (e.g. to support nested data
84
+ # structures), and given that it's called N*M times here, it helps
85
+ # perf significantly to implement the matching bit ourselves.
86
+ if (e === a || a == e)
87
+ expected_matches[ei] << ai
88
+ actual_matches[ai] << ei
89
+ end
90
+ end
91
+ end
92
+
93
+ PairingsMaximizer.new(expected_matches, actual_matches)
94
+ end
95
+ end
96
+
97
+ # Once we started supporting composing matchers, the algorithm for this matcher got
98
+ # much more complicated. Consider this expression:
99
+ #
100
+ # expect(["fool", "food"]).to contain_exactly(/foo/, /fool/)
101
+ #
102
+ # This should pass (because we can pair /fool/ with "fool" and /foo/ with "food"), but
103
+ # the original algorithm used by this matcher would pair the first elements it could
104
+ # (/foo/ with "fool"), which would leave /fool/ and "food" unmatched. When we have
105
+ # an expected elements which is a matcher that matches a superset of actual items
106
+ # compared to another expected element matcher, we need to consider every possible pairing.
107
+ #
108
+ # This class is designed to maximize the number of actual/expected pairings -- or,
109
+ # conversely, to minimize the number of unpaired items. It's essentially a brute
110
+ # force solution, but with a few heuristics applied to reduce the size of the
111
+ # problem space:
112
+ #
113
+ # * Any items which match none of the items in the other list are immediately
114
+ # placed into the `unmatched_expected_indexes` or `unmatched_actual_indexes` array.
115
+ # The extra items and missing items in the matcher failure message are derived
116
+ # from these arrays.
117
+ # * Any items which reciprocally match only each other are paired up and not
118
+ # considered further.
119
+ #
120
+ # What's left is only the items which match multiple items from the other list
121
+ # (or vice versa). From here, it performs a brute-force depth-first search,
122
+ # looking for a solution which pairs all elements in both lists, or, barring that,
123
+ # that produces the fewest unmatched items.
124
+ #
125
+ # @private
126
+ class PairingsMaximizer
127
+ Solution = Struct.new(:unmatched_expected_indexes, :unmatched_actual_indexes,
128
+ :indeterminate_expected_indexes, :indeterminate_actual_indexes) do
129
+ def worse_than?(other)
130
+ unmatched_item_count > other.unmatched_item_count
131
+ end
132
+
133
+ def candidate?
134
+ indeterminate_expected_indexes.empty? &&
135
+ indeterminate_actual_indexes.empty?
136
+ end
137
+
138
+ def ideal?
139
+ candidate? && (
140
+ unmatched_expected_indexes.empty? ||
141
+ unmatched_actual_indexes.empty?
142
+ )
143
+ end
144
+
145
+ def unmatched_item_count
146
+ unmatched_expected_indexes.count + unmatched_actual_indexes.count
147
+ end
148
+
149
+ def +(derived_candidate_solution)
150
+ self.class.new(
151
+ unmatched_expected_indexes + derived_candidate_solution.unmatched_expected_indexes,
152
+ unmatched_actual_indexes + derived_candidate_solution.unmatched_actual_indexes,
153
+ # Ignore the indeterminate indexes: by the time we get here,
154
+ # we've dealt with all indeterminates.
155
+ [], []
156
+ )
157
+ end
158
+ end
159
+
160
+ attr_reader :expected_to_actual_matched_indexes, :actual_to_expected_matched_indexes, :solution
161
+
162
+ def initialize(expected_to_actual_matched_indexes, actual_to_expected_matched_indexes)
163
+ @expected_to_actual_matched_indexes = expected_to_actual_matched_indexes
164
+ @actual_to_expected_matched_indexes = actual_to_expected_matched_indexes
165
+
166
+ unmatched_expected_indexes, indeterminate_expected_indexes =
167
+ categorize_indexes(expected_to_actual_matched_indexes, actual_to_expected_matched_indexes)
168
+
169
+ unmatched_actual_indexes, indeterminate_actual_indexes =
170
+ categorize_indexes(actual_to_expected_matched_indexes, expected_to_actual_matched_indexes)
171
+
172
+ @solution = Solution.new(unmatched_expected_indexes, unmatched_actual_indexes,
173
+ indeterminate_expected_indexes, indeterminate_actual_indexes)
174
+ end
175
+
176
+ def find_best_solution
177
+ return solution if solution.candidate?
178
+ best_solution_so_far = NullSolution
179
+
180
+ expected_index = solution.indeterminate_expected_indexes.first
181
+ actuals = expected_to_actual_matched_indexes[expected_index]
182
+
183
+ actuals.each do |actual_index|
184
+ solution = best_solution_for_pairing(expected_index, actual_index)
185
+ return solution if solution.ideal?
186
+ best_solution_so_far = solution if best_solution_so_far.worse_than?(solution)
187
+ end
188
+
189
+ best_solution_so_far
190
+ end
191
+
192
+ private
193
+
194
+ # Starting solution that is worse than any other real solution.
195
+ NullSolution = Class.new do
196
+ def self.worse_than?(other); true; end
197
+ end
198
+
199
+ def categorize_indexes(indexes_to_categorize, other_indexes)
200
+ unmatched = []
201
+ indeterminate = []
202
+
203
+ indexes_to_categorize.each_pair do |index, matches|
204
+ if matches.empty?
205
+ unmatched << index
206
+ elsif !reciprocal_single_match?(matches, index, other_indexes)
207
+ indeterminate << index
208
+ end
209
+ end
210
+
211
+ return unmatched, indeterminate
212
+ end
213
+
214
+ def reciprocal_single_match?(matches, index, other_list)
215
+ return false unless matches.one?
216
+ other_list[matches.first] == [index]
217
+ end
218
+
219
+ def best_solution_for_pairing(expected_index, actual_index)
220
+ modified_expecteds = apply_pairing_to(
221
+ solution.indeterminate_expected_indexes,
222
+ expected_to_actual_matched_indexes, actual_index)
223
+
224
+ modified_expecteds.delete(expected_index)
225
+
226
+ modified_actuals = apply_pairing_to(
227
+ solution.indeterminate_actual_indexes,
228
+ actual_to_expected_matched_indexes, expected_index)
229
+
230
+ modified_actuals.delete(actual_index)
231
+
232
+ solution + self.class.new(modified_expecteds, modified_actuals).find_best_solution
233
+ end
234
+
235
+ def apply_pairing_to(indeterminates, original_matches, other_list_index)
236
+ indeterminates.inject({}) do |accum, index|
237
+ accum[index] = original_matches[index] - [other_list_index]
238
+ accum
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
@@ -6,15 +6,54 @@ module RSpec
6
6
  actual == expected
7
7
  end
8
8
 
9
- def failure_message_for_should
10
- "\nexpected: #{expected.inspect}\n got: #{actual.inspect}\n\n(compared using ==)\n"
9
+ def failure_message
10
+ "\nexpected: #{format_object(expected)}\n got: #{format_object(actual)}\n\n(compared using ==)\n"
11
11
  end
12
12
 
13
- def failure_message_for_should_not
14
- "\nexpected: value != #{expected.inspect}\n got: #{actual.inspect}\n\n(compared using ==)\n"
13
+ def failure_message_when_negated
14
+ "\nexpected: value != #{format_object(expected)}\n got: #{format_object(actual)}\n\n(compared using ==)\n"
15
+ end
16
+
17
+ def description
18
+ "#{name_to_sentence} #{@expected.inspect}"
15
19
  end
16
20
 
17
21
  def diffable?; true; end
22
+
23
+ private
24
+
25
+ def format_object(object)
26
+ if Time === object
27
+ format_time(object)
28
+ elsif defined?(DateTime) && DateTime === object
29
+ format_date_time(object)
30
+ else
31
+ object.inspect
32
+ end
33
+ end
34
+
35
+ TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
36
+
37
+ if Time.method_defined?(:nsec)
38
+ def format_time(time)
39
+ time.strftime("#{TIME_FORMAT}.#{"%09d" % time.nsec} %z")
40
+ end
41
+ else # for 1.8.7
42
+ def format_time(time)
43
+ time.strftime("#{TIME_FORMAT}.#{"%06d" % time.usec} %z")
44
+ end
45
+ end
46
+
47
+ DATE_TIME_FORMAT = "%a, %d %b %Y %H:%M:%S.%N %z"
48
+ # ActiveSupport sometimes overrides inspect. If `ActiveSupport` is
49
+ # defined use a custom format string that includes more time precision.
50
+ def format_date_time(date_time)
51
+ if defined?(ActiveSupport)
52
+ date_time.strftime(DATE_TIME_FORMAT)
53
+ else
54
+ date_time.inspect
55
+ end
56
+ end
18
57
  end
19
58
  end
20
59
  end
@@ -6,11 +6,11 @@ module RSpec
6
6
  actual.eql? expected
7
7
  end
8
8
 
9
- def failure_message_for_should
9
+ def failure_message
10
10
  "\nexpected: #{expected.inspect}\n got: #{actual.inspect}\n\n(compared using eql?)\n"
11
11
  end
12
12
 
13
- def failure_message_for_should_not
13
+ def failure_message_when_negated
14
14
  "\nexpected: value != #{expected.inspect}\n got: #{actual.inspect}\n\n(compared using eql?)\n"
15
15
  end
16
16
 
@@ -6,21 +6,15 @@ module RSpec
6
6
  actual.equal? expected
7
7
  end
8
8
 
9
- def failure_message_for_should
10
- return <<-MESSAGE
11
-
12
- expected #{inspect_object(expected)}
13
- got #{inspect_object(actual)}
14
-
15
- Compared using equal?, which compares object identity,
16
- but expected and actual are not the same object. Use
17
- `#{eq_expression}` if you don't care about
18
- object identity in this example.
19
-
20
- MESSAGE
9
+ def failure_message
10
+ if expected_is_a_literal_singleton?
11
+ simple_failure_message
12
+ else
13
+ detailed_failure_message
14
+ end
21
15
  end
22
16
 
23
- def failure_message_for_should_not
17
+ def failure_message_when_negated
24
18
  return <<-MESSAGE
25
19
 
26
20
  expected not #{inspect_object(actual)}
@@ -31,16 +25,39 @@ Compared using equal?, which compares object identity.
31
25
  MESSAGE
32
26
  end
33
27
 
34
- def diffable?; true; end
28
+ def diffable?
29
+ !expected_is_a_literal_singleton?
30
+ end
35
31
 
36
32
  private
37
33
 
38
- def inspect_object(o)
39
- "#<#{o.class}:#{o.object_id}> => #{o.inspect}"
34
+ LITERAL_SINGLETONS = [true, false, nil]
35
+
36
+ def expected_is_a_literal_singleton?
37
+ LITERAL_SINGLETONS.include?(expected)
38
+ end
39
+
40
+ def simple_failure_message
41
+ "\nexpected #{expected.inspect}\n got #{inspect_object(actual)}\n"
40
42
  end
41
43
 
42
- def eq_expression
43
- Expectations::Syntax.positive_expression("actual", "eq(expected)")
44
+ def detailed_failure_message
45
+ return <<-MESSAGE
46
+
47
+ expected #{inspect_object(expected)}
48
+ got #{inspect_object(actual)}
49
+
50
+ Compared using equal?, which compares object identity,
51
+ but expected and actual are not the same object. Use
52
+ `expect(actual).to eq(expected)` if you don't care about
53
+ object identity in this example.
54
+
55
+ MESSAGE
56
+ end
57
+
58
+
59
+ def inspect_object(o)
60
+ "#<#{o.class}:#{o.object_id}> => #{o.inspect}"
44
61
  end
45
62
  end
46
63
  end