【问题标题】:Ruby indented multiline strings [duplicate]Ruby缩进多行字符串[重复]
【发布时间】:2011-03-22 00:11:53
【问题描述】:

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

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

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

使用&lt;&lt;-,我可以在此处缩进文档标记,但它并没有去除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

有没有办法测试这种真正可读的多行字符串?

【问题讨论】:

标签: ruby code-formatting


【解决方案1】:

如果您正在构建 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

【讨论】:

    【解决方案2】:

    就我个人而言,我认为 Ruby 的缩进 heredocs 是无用的,它们应该更像 Bash 缩进的 heredocs 并且还去除字符串 inside 的空格……

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

    【讨论】:

      【解决方案3】:

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

      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 部分。

      【讨论】:

        【解决方案4】:

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

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

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

        我认为测试整个多行文本块根本不是一个好习惯。

        【讨论】:

        • 一次性测试是唯一正确的方法。如果它不同 - 测试应该显示整个预期结果和整个实际结果。逐行比较等只会使测试失败报告毫无价值。
        • 正如我在回答中所说,这实际上取决于您要测试的内容:内容还是格式?如果你真的想同时测试两者,我认为其他人已经给出了很好的答案。
        猜你喜欢
        • 1970-01-01
        • 2017-07-11
        • 1970-01-01
        • 2016-02-05
        • 2011-01-31
        • 2021-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多