【问题标题】:Is it idiomatic Ruby to add an assert( ) method to Ruby's Kernel class?将 assert() 方法添加到 Ruby 的 Kernel 类中是惯用的 Ruby 吗?
【发布时间】:2010-09-13 23:04:10
【问题描述】:

我正在通过用 Ruby 编写与 Kent Beck 的 xUnit 等效的代码来扩展我对 Ruby 的理解。 Python(由 Kent 编写)在广泛使用的语言中有一个 assert() 方法。红宝石没有。我认为添加它应该很容易,但内核是放置它的正确位置吗?

顺便说一句,我知道 Ruby 中存在各种 Unit 框架 - 这是一个学习 Ruby 习语的练习,而不是“完成某事”。

【问题讨论】:

  • 我喜欢两者兼得的问题:教你,然后完成任务。 :)

标签: ruby assert xunit


【解决方案1】:

不,这不是最佳做法。与 Ruby 中的 assert() 最好的类比就是提高

 raise "This is wrong" unless expr

如果您想提供更具体的异常处理,您可以实现自己的异常

【讨论】:

  • 如果你愿意,你也可以使用fail if [expr],这会引发运行时错误。
  • failraise 的别名,不是吗?他们都给RuntimeError。 (也许一个更惯用?)
  • 如果您的软件在生产中快速而严重地失败,这很好,我认为静默异常是“捕获所有异常”解决方法的可怕副本。它通常是在糟糕的代码出现问题时应用的第一个糟糕的创可贴。
  • 断言和异常是不同的东西。
  • @nus 断言不是为了警告,而是为了确保某事在那个地方总是为真。如果您的断言在生产中失败,您肯定想中止。
【解决方案2】:

我认为在 Ruby 中使用断言是完全有效的。但是您提到了两件事:

  • xUnit 框架使用assert 方法来检查您的测试预期。它们旨在用于您的测试代码,而不是您的应用程序代码。
  • 某些语言(如 C、Java 或 Python)包含一个 assert 构造,旨在用于您的程序代码中,以检查您对其完整性所做的假设。这些检查是在代码本身内部构建的。它们不是测试时实用程序,而是开发时实用程序。

我最近写了solid_assert: a little Ruby library implementing a Ruby assertion utilitya post in my blog explaining its motivation.. 它让你可以写出以下形式的表达式:

assert some_string != "some value"
assert clients.empty?, "Isn't the clients list empty?"

invariant "Lists with different sizes?" do
    one_variable = calculate_some_value
    other_variable = calculate_some_other_value
    one_variable > other_variable
end    

它们可以被停用,因此assertinvariant 被评估为空语句。这可以让您避免生产中的任何性能问题。但请注意The Pragmatic Programmers 建议不要停用它们。仅当它们确实影响性能时才应停用它们。

关于答案说惯用的Ruby方式是使用普通的raise语句,我认为它缺乏表现力。断言编程的黄金法则之一是不使用断言进行正常的异常处理。它们是两种完全不同的东西。如果你对它们两个使用相同的语法,我认为你的代码会更加晦涩难懂。当然,您将失去停用它们的能力。

您可以确信使用断言是一件好事,因为像 The Pragmatic Programmer From Journeyman to MasterCode Complete 这样的两本必读经典书籍专门为它们提供了整个部分并推荐使用它们。还有一篇名为Programming with assertions 的好文章很好地说明了断言编程的意义以及何时使用它(它基于 Java,但概念适用于任何语言)。

【讨论】:

  • 我还建议 Steve Maguire 的“Writing Solid Code”,它非常面向 C,但谈到了断言、测试策略和构造函数的想法,这些函数也适用于方法构造。跨度>
【解决方案3】:

您将 assert 方法添加到内核模块的原因是什么?为什么不直接使用另一个名为 Assertions 的模块?

像这样:

module Assertions
  def assert(param)
    # do something with param
  end

  # define more assertions here
end

如果您确实需要您的断言无处不在,请执行以下操作:

class Object
  include Assertions
end

免责声明:我没有测试代码,但原则上我会这样做。

【讨论】:

  • 这不是猴子补丁吗?您正在更改 Object 类。
  • 是的,这是经典的猴子补丁。这就是这样做的方法,如果你真的必须让assert 方法在全球范围内可用。但是,我不建议任何人在非玩具项目中使用这样的猴子补丁。
【解决方案4】:

这不是特别地道,但我认为这是个好主意。特别是如果这样做:

def assert(msg=nil)
    if DEBUG
        raise msg || "Assertion failed!" unless yield
    end
end

这样,如果您决定不设置 DEBUG(或其他一些方便的开关,我过去使用过 Kernel.do_assert),则不会产生任何影响。

【讨论】:

    【解决方案5】:

    我的理解是,您编写自己的测试套件是为了更加熟悉 Ruby。因此,虽然 Test::Unit 可能作为指南有用,但它可能不是您想要的(因为它已经完成了工作)。

    也就是说,python 的断言(至少对我而言)更类似于 C 的 assert(3)。它不是专门为单元测试而设计的,而是为了捕捉“这永远不应该发生”的情况。

    Ruby 的内置单元测试如何看待问题,那么,每个单独的测试用例类都是TestCase 的子类,其中包括一个“断言”语句,用于检查传递给的内容的有效性它并记录下来以供报告。

    【讨论】:

      猜你喜欢
      • 2016-04-04
      • 1970-01-01
      • 1970-01-01
      • 2023-02-10
      • 1970-01-01
      • 1970-01-01
      • 2013-07-23
      • 2012-11-22
      • 1970-01-01
      相关资源
      最近更新 更多