关于防御性编程:测试用例和断言语句

关于防御性编程:测试用例和断言语句

Test Cases AND assertion statements

这个问题中的代码让我思考

1
2
3
4
5
assert(value>0); //Precondition
if (value>0)
{
  //Doit
}

我从不写if语句。 声明就足够了/您可以做的一切。
"早期崩溃,经常崩溃"

CodeComplete状态:

  • 断言语句使应用程序正确
  • if-test使应用程序健壮

我认为您没有通过更正无效的输入值或跳过代码来使应用程序更强大:

1
2
3
4
5
6
7
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:
"当我们不仅仅依赖于断言时,现实世界的程序就会变得过于混乱。"

问题:我错了,顽强,愚蠢,太缺乏防御性了吗?


仅信任断言的问题在于它们可能在生产环境中被关闭。引用维基百科文章:

Most languages allow assertions to be
enabled or disabled globally, and
sometimes independently. Assertions
are often enabled during development
and disabled during final testing and
on release to the customer. Not
checking assertions avoiding the cost
of evaluating the assertions while,
assuming the assertions are free of
side effects, still producing the same
result under normal conditions. Under
abnormal conditions, disabling
assertion checking can mean that a
program that would have aborted will
continue to run. This is sometimes
preferable.
Wikipedia

因此,如果代码的正确性依赖于Asserts,那么您可能会遇到严重的问题。当然,如果代码在测试中起作用了,那么它应该在生产中起作用...现在输入第二个可以处理该代码的家伙,这将解决一个小问题...


使用断言来验证您控制的输入:私有方法等。

使用if语句来验证您无法控制的输入:设计用于用户使用的公共接口,用户输入测试等。

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


在某些情况下,在构建发行版时断言被禁用。您可能对此没有控制权(否则,可以在asserts的基础上进行构建),因此这样做是个好主意。

"校正"输入值的问题在于调用者将无法获得他们期望的结果,这可能导致问题甚至在程序的完全不同部分崩溃,从而使调试成为噩梦。

我通常在if语句中引发异常,以在断言被禁用的情况下代替断言的角色

1
2
3
assert(value>0);
if(value<=0) throw new ArgumentOutOfRangeException("value");
//do stuff

我不同意这一说法:

Only the caller knows what"a valid
input value" is for your function, and
he must check its validity before he
calls your function.

呼叫者可能认为他知道输入值正确。只有方法作者知道它应该如何工作。程序员的最佳目标是使客户陷入"成功之坑"。您应确定在特定情况下哪种行为更合适。在某些情况下,错误的输入值是可以原谅的,在其他情况下,您应该抛出异常返回错误。

至于Asserts,我会重复其他评论者,assert是针对代码作者而不是代码客户端的调试时间检查。


不要忘记,大多数语言都允许您关闭断言...就我个人而言,如果我准备编写测试来防止所有范围的无效输入的测试,那么我一开始就不会打扰断言。

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


如果我没记错CS课

前提条件定义在什么条件下定义函数的输出。如果使函数处理错误条件,则将为这些条件定义函数,并且不需要assert语句。

所以我同意。通常,您不需要两者。

正如Rik所说,如果删除已发布代码中的断言,这可能会导致问题。通常,除了在性能至关重要的地方,我不会这样做。


我应该说过,我知道断言(在这里)在生产代码中消失的事实。

如果if语句实际上纠正了生产代码中的无效输入数据,则意味着断言在调试代码的测试过程中永远不会消失,这意味着您编写了从未执行的代码。

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

(引用安德鲁)"防止所有范围的无效输入,首先我不会理会断言。" ->编写一个if-test。

(引用aku)"错误的输入值是可以原谅的"->写一个断言。

我都受不了...


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

检查具有if条件的外部输入。从外部看,这不在您/您的团队控制和测试的代码之外。

(可选)您可以同时拥有两者。这将用于外部功能,这些功能将在生产前进行集成测试。


断言的一个问题是它们可以(并且通常会)从代码中进行编译,因此您需要添加两面墙,以防编译器扔掉它们。


推荐阅读