Module: MiniTest::Assertions

Defined in:
lib/quality_extensions/test/difference_highlighting-minitest.rb

Constant Summary collapse

@@use_assert_equal_with_highlight =
nil

Instance Method Summary collapse

Instance Method Details

#assert_equal_with_highlighting(expected, actual, options = {}) ⇒ Object

The built-in behavior for assert_equal method is great for comparing small strings, but not so great for long strings. If both the strings you are dealing with are both 20 paragraphs long, for example, and they differ by only one character, the task of locating and identifying the one character that is off is akin to finding a (literal) needle in a (literal) haystack (not fun!).

This replacement/wrapper for assert_equal aims to solve all of your string comparison woes (and eventually someday perhaps arrays and hashes as well), helping you to spot differences very quickly and to have fun doing it.

Rather than simply showing you the raw (inspected) expected and actual and expecting you, the poor user, to painstakingly compare the two and figure out exactly which characters are different by yourself this method will highlight the differences for you, allowing you to spot them an instant or less!!

Strings:

  • Does a characterwise comparison between the two strings. That is, for each index, it will look at the character at that index and decide if it is the same or different than the character at the same location in the other string. There are 3 1/2 cases:

    • Common characters are displayed in green,

    • Different characters in red,

    • Characters that exist in only one string but not the other are displayed in yellow

      • A cyan ~ will appear that location in the other string, as a placeholder for the missing character.

Arrays:

  • :todo:

Hashes:

  • :todo:

Disabling/enabling highlighting:

By default, highlighting is only used when one or both of the strings being compared is long or spans multiple lines. You can override the default with the :higlight option:

assert_equal 'really long string', 'another really long string', :highlight => true

You can turn it on for all assert_equal assertions by calling

MiniTest::Assertions::use_assert_equal_with_highlight!

Or you can just turn it on or off for the duration of a block only:

MiniTest::Assertions::use_assert_equal_with_highlight! do
  assert_equal 'really long string', 'another really long string'
end

Notes:

  • Spaces are displayed as with a bright colored background so that they are actually visible on the screen (so you can distinguish an empty line from a line with spaces on it, for example).

  • Newlines are displayed as the text \n followed by the actual newline. Other control characters (\t, \r) are escaped as well so that you can tell what character it is.

Difference in method signature from assert_equal_without_difference_highlighting (the standard behavior):

  • The last argument (options) is expected to be a hash rather than message=nil, since I don’t see the use in passing in a message if the default message can be made useful enough.

  • However, for compatibility with existing assert_equal calls, it will check if the 3rd argument is a string and if it is will use it as the failure message.

  • If you to pass in a message in combination with other options, use :message => 'my message'

Advanced:

  • If you want everything to be escaped (so you can see the color codes instead of the color itself, for example), use :inspect_strings => true



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/quality_extensions/test/difference_highlighting-minitest.rb', line 155

def assert_equal_with_highlighting(expected, actual, options = {})
  if options.is_a?(String)
    message = options 
    options = {}
  else
    message = options.delete(:message) || nil
  end
  highlight = options.delete(:highlight)

  Assertions.send_unless(highlight.nil?, :use_assert_equal_with_highlight!, highlight) do

    if String===expected and String===actual and expected!=actual and 
        (Assertions.use_assert_equal_with_highlight? || [expected.length, actual.length].max > 80 || [expected, actual].any? {|a| a.include?("\n")}) and
        !(Assertions.use_assert_equal_with_highlight? == false)

      expected_with_highlighting = ''
      actual_with_highlighting = ''
      full_message = nil
      String.color_on! do
        longest_string = [expected, actual].max {|a, b| a.length <=> b.length}
        longest_string.each_char_with_index do |i, exp|
          exp = expected[i] ? expected[i].chr : nil
          act = actual[i]   ? actual[i].chr   : nil
          if act.nil?
            expected_with_highlighting << exp.highlight_unique
            actual_with_highlighting   << '~'.highlight_absence
          elsif exp.nil?
            expected_with_highlighting << '~'.highlight_absence
            actual_with_highlighting   << act.highlight_unique
          elsif exp != act
            expected_with_highlighting << exp.highlight_difference
            actual_with_highlighting   << act.highlight_difference
          else
            expected_with_highlighting << exp.highlight_commonality
            actual_with_highlighting   << exp.highlight_commonality
          end

        end
        full_message = message(message) { <<End }
#{(' '*50 + ' Expected: ' + ' '*50).blue.underline.on_white }
#{expected_with_highlighting}
#{(' '*50 + ' But was:  ' + ' '*50).yellow.underline.on_red }
#{actual_with_highlighting}
End
      end
        MiniTest.inspect_strings!(options.delete(:inspect_strings) || false) do
          assert(expected == actual, full_message)
        end
    else
      assert_equal_without_highlighting(expected, actual, message)
    end

  end # use_assert_equal_with_highlight!

end

#mu_pp_with_option_to_not_use_inspect_for_strings(object) ⇒ Object

The problem with the original convert() is that it always called #inspect on strings… which is fine if you really want to see all those n‘s and such. But not so great if you want to visually compare the strings. And if you have ANSI color codes in the strings, it will escape those so that you see the codes (e[33m1) rather than the nice colored strings that you (sometimes) want to see…



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/quality_extensions/test/difference_highlighting-minitest.rb', line 82

def mu_pp_with_option_to_not_use_inspect_for_strings(object)
  if String === object
    if MiniTest.inspect_strings?
      # Use the original method, which used inspect
      mu_pp_without_option_to_not_use_inspect_for_strings(object)
    else
      object
    end
  else
    # We only care about strings. Everything else can just keep happening like it was before.
    mu_pp_without_option_to_not_use_inspect_for_strings(object)
  end
end