在Ruby的Kernel类中添加assert()方法是否是惯用的Ruby?


76

我通过在Ruby中编码等效于Kent Beck的xUnit来扩展我对Ruby的理解。Python(由Kent编写)以广泛使用的语言提供了assert()方法。Ruby没有。我认为添加它应该很容易,但是Kernel是否正确放置它?

顺便说一句,我知道Ruby中存在各种Unit框架-这是一种学习Ruby习惯用法的练习,而不是“完成某些工作”。


5
我喜欢同时兼有的问题:教导您并做点事情。:)
伊恩·泰瑞尔

Answers:


108

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

 raise "This is wrong" unless expr

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


29
fail if [expr]如果愿意,还可以使用引发运行时错误。
熊加米奥夫

6
fail是的别名raise,不是吗?他们俩都给RuntimeError。(也许是更惯用了吗?)
本杰明·奥克斯

5
如果您的软件在生产中快速失败,那很好,我认为静默异常是“捕获所有异常”解决方法的糟糕副本。通常,这是事情破裂时应用于可怕代码的第一个可怕创可贴。
朱利克

2
断言和异常是不同的东西。
Jack Casey 2013年

6
@nus断言不是用于警告,而是用于确保该位置处的某些情况始终正确。如果断言在生产中失败,那么您绝对想中止。
Yarin 2014年

30

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

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

我最近写了solid_assert:一个小的Ruby库,实现了Ruby断言实用程序,并且在我的博客中发表了一篇文章,解释了它的动机..它使您可以用以下形式编写表达式:

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以空语句的形式进行评估。这样可以避免生产中出现任何性能问题。但是请注意,实用程序员建议不要停用它们。仅当它们确实影响性能时,才应停用它们。

关于回答说惯用的Ruby方法使用的是普通raise语句,我认为它缺乏表达能力。断言编程的黄金法则之一是不使用断言进行常规异常处理。它们是完全不同的两件事。如果您对两者使用相同的语法,我认为您的代码会更加晦涩难懂。当然,您会失去激活它们的能力。

您可以确信使用断言是一件好事,因为两本必读的经典书籍,例如《从程序员到熟练的实用程序员》《代码完整》,全书都专门针对它们,并推荐它们的使用。还有一篇名为“带断言的编程”的好文章,很好地说明了什么是断言编程以及何时使用它(基于Java,但是概念适用于任何语言)。


我还建议使用Steve Maguire编写的“ Writing Solid Code”,它是非常面向C的,但是要讨论断言,测试策略以及关于构造也适用于方法构造的函数的想法。
Paul Kroll 2014年

14

您将assert方法添加到内核模块的原因是什么?为什么不只使用另一个称为Assertions或的模块呢?

像这样:

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

  # define more assertions here
end

如果您确实需要断言在任何地方都可以使用请执行以下操作:

class Object
  include Assertions
end

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


1
这不是猴子在打补丁吗?您正在更改Object班级。
tsikov '16

是的,那是经典的猴子修补程序。如果您确实必须使该assert方法在全球范围内可用,那就是这样做的方法。但是,我不建议任何人在非玩具项目中使用这种猴子修补程序。
Christoph Schiessl

8

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

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

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


5

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

也就是说,python的assert(至少对我而言)与C的assert(3)更相似。它不是专门为单元测试而设计的,而是用来捕获“绝不应该发生”的情况。

Ruby的内置单元测试如何倾向于看待该问题,那么,每个单独的测试用例类都是TestCase的子类,并且包含“ assert”语句,该语句检查传递给它的内容的有效性并记录为报告。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.