测试用例和断言语句

12

阅读这个问题的代码让我思考。

assert(value>0); //Precondition
if (value>0)
{
  //Doit
}

我从不写if语句。断言足以/你唯一可以做的就是断言。

“早崩溃,经常崩溃”

CodeComplete指出:

  • Assert语句使应用程序保持正确性
  • If测试使应用程序变得健壮

我认为通过纠正无效的输入值或跳过代码并不能使应用程序更加健壮:

assert(value >= 0 );  //Precondition
assert(value <= 90);  //Precondition
if(value < 0)         //Just in case
  value = 0;
if (value > 90)       //Just in case
  value = 90;
//Doit

这些更正是基于您对外部世界所做的假设而进行的。 只有调用者知道对于您的函数来说什么才是“有效的输入值”,他必须在调用您的函数之前检查其有效性。

为了转述CodeComplete的话: 当我们不仅仅依赖断言时,现实世界中的程序变得太乱了。

问题:我是不是错了,固执,愚蠢,过于非守势......


我同意你的观点。断言的整个目的是在测试期间执行完整性检查,这些检查在生产中执行起来太昂贵了。它们的功能与异常根本不同。 - Galik
9个回答

10
只信赖Asserts的问题在于它们可能在生产环境中被关闭。引用维基百科文章的话来说:
大多数语言允许全局启用或禁用断言,有时是独立的。在开发期间通常启用断言,在最终测试和交付给客户时禁用断言。不检查断言可以避免评估断言的成本,同时假设断言没有副作用,在正常条件下仍然产生相同的结果。在异常情况下,禁用断言检查可能意味着原本会中止的程序将继续运行。这有时是更可取的。 维基百科 因此,如果您的代码正确性依赖于Asserts的存在,您可能会遇到严重的问题。当然,如果代码在测试期间工作,则应在生产期间工作...现在进入第二个修复小问题的人...

我认为问题可能出在修复小问题的人决定在发货前不测试软件。测试阶段不能明智地“跳过”,因此断言也不能跳过。 - Galik

4

对于您控制的输入,例如私有方法等,请使用断言进行验证。

对于您无法控制的输入,例如面向用户消费的公共接口、用户输入测试等,请使用if语句进行验证。

在应用程序中内置断言进行测试。然后在不包含断言的情况下进行部署。


3
在某些情况下,在发布时构建时会禁用断言。您可能无法控制此操作(否则,您可以使用断言进行构建),因此按照以下方式执行可能是一个好主意。
“更正”输入值的问题在于调用者将无法得到他们期望的结果,这可能会导致程序的其他部分出现问题或甚至崩溃,使得调试成为一场噩梦。
我通常在if语句中抛出异常,以在禁用断言时接管assert的角色。
assert(value>0);
if(value<=0) throw new ArgumentOutOfRangeException("value");
//do stuff

2
我喜欢抛出异常:它总是有效的(无论是生产环境还是调试环境),但为什么还要编写断言语句呢?那只能“半数”地起作用。 - jan
这只是一个惯例问题,我认为。当我看到一个未被捕获的异常时,可能有数百万个原因导致它。当我发现一个失败的断言时,我知道是我自己搞砸了,这样更容易找到原因。 - Rik

2
我不同意这个说法:

只有调用者知道你的函数的“有效输入值”是什么,他必须在调用函数之前检查其有效性。

调用者可能认为他知道输入值是否正确。只有方法的作者知道它应该如何工作。程序员的最佳目标是使客户端陷入“成功的陷阱”。您应该决定在给定情况下哪种行为更合适。在某些情况下,不正确的输入值可能是可以原谅的,在其他情况下,您应该抛出异常或返回错误。

至于断言,我想重复其他评论者的观点,断言是代码作者进行调试时检查的,而不是代码客户端。


1

如果我没记错的话,这是计算机科学课上讲的

前置条件定义了函数输出所依赖的条件。如果你的函数处理错误条件,那么你的函数在这些条件下都能被定义,并且你就不需要断言语句。

所以我同意。通常情况下你不需要两者都用。

正如Rik所评论的,如果在发布代码中删除断言,可能会导致问题。通常我只在性能关键的地方这样做。


这种方法的问题在于,非法调用导致的崩溃可能发生在程序的完全不同部分,这使得调试变成了一场噩梦。 - Rik
我同意。我想我的回答不完整。通常我不会在发布的代码中删除断言。我会修正我的回答。 - Mendelt

1

不要忘记,大多数编程语言都允许你关闭断言...就我个人而言,如果我准备编写if测试来保护所有无效输入范围,我就不会在第一时间使用断言。

另一方面,如果你没有编写处理所有情况的逻辑(可能是因为尝试继续处理无效输入是不明智的),那么我会使用断言语句并采用“尽早失败”的方法。


0

对于仅由您使用的内部函数,请仅使用断言。 断言将有助于在测试期间捕获错误,但不会影响生产性能。

使用if条件语句检查来自外部的输入。 外部是指您/您的团队无法控制和测试的任何地方。

可选地,您可以同时使用两者。 这将用于面向外部的函数,在生产之前进行集成测试。


0

我应该声明一下,我知道在生产代码中,断言(这里)会消失。

如果if语句在生产代码中实际上纠正了无效的输入数据,这意味着在调试代码测试期间,断言从未触发过,这意味着你编写了从未执行过的代码。

对我来说,这是一个OR情况:

(引用Andrew)“防止所有范围的无效输入,我不会在第一时间使用断言。” ->编写if-test。

(引用aku)“不正确的输入值可能是可原谅的” ->编写assert。

我两个都不能忍受...


-1
断言的问题在于它们可以(并且通常会)被编译器从代码中删除,因此您需要添加两个墙壁以防止编译器将其中一个丢弃。

断言在生产代码中被“设计”移除。这是一项功能,而不是错误。您只需要适当地使用它们。如果您想在生产代码中进行测试,请不要使用断言。 - Galik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接