Ruby 缩进多行字符串

发布于 2024-09-12 10:15:39 字数 843 浏览 2 评论 0原文

这是一个最佳实践问题。有很多明显的方法可以做到这一点,但没有一个看起来完全正确。

我经常需要测试是否生成了一些多行字符串。这通常会破坏缩进,使一切看起来一团糟:

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

使用 <<- 我可以在此处缩进文档标记,但它不会删除heredoc内的缩进,它看起来仍然很糟糕。

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

这让我可以缩进,但测试行的可读性会受到影响。这个gsub在这里真的感觉不太对劲。

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

有什么方法可以测试这样的多行字符串是否真的可读?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

绾颜 2024-09-19 10:15:39

如果您正在构建 Rails 应用程序,请尝试使用 strip_heredoc,否则您可能始终需要 active_support 核心扩展。

您的示例可能如下所示:

require 'active_support/core_ext'

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

如果您确实不想包含它们,请从 active_support 复制以下代码作为如何处理格式的示例。

class String
  def try(*a, &b)
    if a.empty? && block_given?
      yield self
    else
      __send__(*a, &b)
    end
  end

  def strip_heredoc
    indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
    gsub(/^[ \t]{#{indent}}/, '')
  end
end

If you're building a Rails application, try using strip_heredoc, if not you could always require active_support core extensions.

Your Example might look like this:

require 'active_support/core_ext'

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

If you really don't want to include them, the following code is copied from active_support as and example of how you might handle the formatting.

class String
  def try(*a, &b)
    if a.empty? && block_given?
      yield self
    else
      __send__(*a, &b)
    end
  end

  def strip_heredoc
    indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
    gsub(/^[ \t]{#{indent}}/, '')
  end
end
难理解 2024-09-19 10:15:39

就我个人而言,我认为 Ruby 的缩进此处文档毫无用处,它们应该更像 Bash 缩进此处文档,并且还去除字符串内的空格……

无论如何,有几个库尝试处理这种情况。有很多库试图解决此问题:

Personally, I think that Ruby's indented heredocs are useless and they should work more like Bash indented heredocs and also strip whitespace inside the string …

Anyway, there are a couple of libraries that try to deal with this situation. There is a wide array of libraries that try to fix this problem:

风透绣罗衣 2024-09-19 10:15:39

我不确定这些是否可以称为“最佳实践”,但这里有四种可能性

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

全部通过

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

4 tests, 4 assertions, 0 failures, 0 errors

我个人更喜欢嵌入换行符的字符串(方法2)除非字符串很长在这种情况下我会使用DATA部分。

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.

疧_╮線 2024-09-19 10:15:39

是关于格式的测试还是关于内容的测试?

如果这是一个关于格式化的测试,也许你的测试水平太高了,你应该测试一个“Formatter”类,你可能会找到一种方法来测试该类,使多行文本比较变得毫无用处。然后,您将能够模拟 Formatter 类以检查它是否会收到所需的所有内容。例如,Formatter 可以是一个具有 add_line 方法的类,该方法在给出的每个参数后添加一个“\n”,以及一个 formatted_string 将返回多行字符串。一旦您测试了 Formatter 类,您只需检查它是否被正确调用。这样,您就可以将内容测试与格式测试分开。

如果是关于内容的测试,也许你应该将 hello_world 按行分割,然后检查第一行是否包含“Hello, world”,第二行包含“World问候你”。

我认为测试整个多行文本块根本不是一个好的做法。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文