Class: Test::Unit::TestCase

Inherits:
Object
  • Object
show all
Defined in:
lib/assert_same.rb

Constant Summary collapse

@@file_offsets =

Hash[line_number] = offset For each line in the original file we store its offset (+N or -N lines) relative to the actual file

Hash.new { |hash, key| hash[key] = {} }

Instance Method Summary collapse

Instance Method Details

#assert_same(actual, expected = :autofill_expected_value) ⇒ Object

assert_same: assert which checks that two strings (expected and actual) are same and which can “magically” replace expected value with the actual in case the new behavior (and new actual value) is correct

Usage ==

Write this in the test source:

assert_same something, <<-END
    foo
    bar
    zee
END

Then run tests as usual:

rake test:units
ruby test/unit/foo_test.rb
...

When assert_same fails, you’ll be able to:

  • review diff

  • (optionally) accept new actual value (this modifies the test source file)

Additional options for test runs: –no-interactive skips all questions and just reports failures –autoaccept prints diffs and automatically accepts all new actual values –no-canonicalize turns off expected and actual value canonicalization (see below for details)

Additional options can be passed during both single test file run and rake test run:

ruby test/unit/foo_test.rb -- --autoaccept
ruby test/unit/foo_test.rb -- --no-interactive

rake test TESTOPTS="-- --autoaccept"
rake test:units TESTOPTS="-- --no-canonicalize --autoaccept"

Canonicalization ==

Before comparing expected and actual strings, assert_same canonicalizes both using these rules:

  • indentation is ignored (except for indentation relative to the first line of the expected/actual string)

  • ruby-style comments after “#” are ignored: both whole-line and end-of-line comments are supported

  • empty lines are ignored

  • trailing whitespaces are ignored

You can turn canonicalization off with –no-canonicalize option. This is useful when you need to regenerate expected test strings. To regenerate the whole test suite, run:

rake test TESTOPTS="-- --no-canonicalize --autoaccept"

Example of assert_same with comments:

assert_same something, <<-END
    # some tree
    foo 1
      foo 1.1
      foo 1.2    # some node
    bar 2
      bar 2.1
END

Umportant Usage Rules ==

Restrictions:

  • only END and EOS are supported as end of string sequence

  • it’s a requirement that you have <<-END at the same line as assert_same

  • assert_same can’t be within a block

Storing expected output in files:

  • assert_same something, :log => <path_to_file>

  • path to file is relative to:

    • RAILS_ROOT (if that is defined)

    • current dir (if no RAILS_ROOT is defined)

  • file doesn’t have to exist, it will be created if necessary

Misc:

  • it’s ok to have several assert_same’s in the same test method, assert_same. correctly updates all assert_same’s in the test file

  • it’s ok to omit expected string, like this:

    assert_same something
    

    in fact, this is the preferred way to create assert_same tests - you write empty assert_same, run tests and they will fill expected values for you automatically



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/assert_same.rb', line 97

def assert_same(actual, expected = :autofill_expected_value)
    if expected.class == String
        expected ||= ""
        mode = :expecting_string
    elsif expected == :autofill_expected_value
        expected = ""
        mode = :autofill_expected_value
    elsif expected.class == Hash
        raise ":log key is missing" unless expected.has_key? :log
        mode = :expecting_file
        log_file = expected[:log]
        if defined? RAILS_ROOT
            log_file = File.expand_path(log_file, RAILS_ROOT)
        else
            log_file = File.expand_path(log_file, Dir.pwd)
        end
        expected = File.exists?(log_file) ? File.read(log_file) : ""
    else
        internal_error("Incorrect expected argument for assert_same. It must be either String or Hash.")
    end

    # interactive mode is turned on by default, except when
    # - --no-interactive is given
    # - STDIN is not a terminal device (i.e. we can't ask any questions)
    interactive = !ARGV.include?("--no-interactive") && STDIN.tty?
    canonicalize = !ARGV.include?("--no-canonicalize")
    autoaccept = ARGV.include?("--autoaccept")

    is_same_canonicalized, is_same, diff_canonicalized, diff = compare_for_assert_same(expected, actual)

    if (canonicalize and !is_same_canonicalized) or (!canonicalize and !is_same)
        diff_to_report = canonicalize ? diff_canonicalized : diff
        if interactive
            # print method name and short backtrace
            failure = Test::Unit::Failure.new(name, filter_backtrace(caller(0)), diff_to_report)
            puts "\n#{failure}"

            if autoaccept
                accept = true
            else
                print "Accept the new value: yes to all, no to all, yes, no? [Y/N/y/n] (y): "
                STDOUT.flush
                response = STDIN.gets.strip
                accept = ["", "y", "Y"].include? response
                ARGV << "--autoaccept" if response == "Y"
                ARGV << "--no-interactive" if response == "N"
            end

            if accept
                if [:expecting_string, :autofill_expected_value].include? mode
                    accept_string(actual, mode)
                elsif mode == :expecting_file
                    accept_file(actual, log_file)
                end
            end
        end
        if accept
            # when change is accepted, we should not report it as a failure because
            # we want the test method to continue executing (in case there're more
            # assert_same's in the method)
            add_assertion
        else
            raise Test::Unit::AssertionFailedError.new(diff_to_report)
        end
    else
        add_assertion
    end
end