tags:

views:

58

answers:

3

It's a best-practice question. There are obvious ways to do that, none of them just seem quite right.

Quite often I need to test that some multi-line string gets produced. This normally breaks indentation making everything look like a mess:

class TestHelloWorld < Test::Unit::TestCase
  def test_hello
    assert_equal <<EOS, hello_world
Hello, world!
  World greets you
EOS
  end
end

With <<- I can indent here doc marker, but it doesn't strip indentation inside heredoc, it still looks horrible.

class TestHelloWorld < Test::Unit::TestCase
  def test_hello
    assert_equal <<-EOS, hello_world
Hello, world!
  World greets you
    EOS
  end
end

This lets me indent but readability of test line suffers. This gsub really doesn't feel right here.

class TestHelloWorld < Test::Unit::TestCase
  def test_hello
    assert_equal <<-EOS.gsub(/^ {6}/, ""), hello_world
      Hello, world!
        World greets you
    EOS
  end
end

Is there any way to test such multi-line strings that's really readable?

A: 

I'm not sure any of these could be called "Best Practice" but here are four possibilities

class Hello

  def self.world
"Hello, world!
  World greets you
"
  end
end

require 'test/unit'

class TestHelloWorld < Test::Unit::TestCase

#Define a constant array of multiline strings and test against the array
# see test_hello_4 
# Alternatively store the multi-line strings in a Yaml fixture file and load 
# them into a constant Hash or Array within a setup method
MLINE = [
"Hello, world!
  World greets you
",
"Another multi line string",
  ]

  # Create a method to return the string
  def test_hello_1
    assert_equal Hello.world, hello_world_string()
  end

  # Create the string using embedded newlines
  def test_hello_2
    assert_equal Hello.world, "Hello, world!\n  World greets you\n"
  end

  # if you only have 1 in the test then put it in a DATA section
  def test_hello_3
    assert_equal Hello.world, DATA.read
  end

  def test_hello_4
    assert_equal Hello.world, MLINE[0]
  end

  def hello_world_string
"Hello, world!
  World greets you
"
  end
end

__END__
Hello, world!
  World greets you

All passing

Loaded suite test_hello_world
Started
....
Finished in 0.00083 seconds.

4 tests, 4 assertions, 0 failures, 0 errors

I would personally prefer the string with embedded newlines (Method 2) unless the string was very long in which case I would go with the DATA section.

Steve Weet
A: 

Is it a test about formatting or about the content ?

If it's a test about formatting, maybe your test is too high level, and you should test a "Formatter" class and you'll probably find a way to test the class in a way that makes the multiline text comparison useless. And then, you would be able to mock the Formatter class to check that it will receive all the content it needs. For example, the Formatter could be a class that have a *add_line* method which adds a "\n" after each argument it is given and a *formatted_string* that will return the multiline string. Once you've tested the Formatter class, you'll just have to check it is called correctly. This way, you separate the tests for the content from the tests for the format.

If it's a test about the content, maybe you should just split the hello_world by line, and then check that the first line contains "Hello, world" and the second one contains "World greets you".

I don't think it's a good practice at all to test a whole multiline block of text.

David
Testing it all at once is the only right way. If it differs - test should show entire expected result, and entire actual result. Line by line comparisons and such just make test fail reports worthless.
taw
As I say in my answer, it really depends on what you are testing : the content or the format ? If you really want to test both at the same time, I think the others already have given good answers.
David
+1  A: 
Jörg W Mittag