8

我正在尝试将一些按合同设计的技术融入我的编码风格。后置条件在我看来很像嵌入式单元测试,我想知道我的想法是在正确的轨道上还是偏离了基础。

维基百科将后置条件定义为“一个条件或谓词,在执行某些代码部分或正式规范中的操作后必须始终为真。后置条件有时使用代码本身中的断言进行测试”。

这与您在直接验证状态(不使用模拟)的单元测试中所做的不太相似吗?

如果是这样的话:

1)通过使用后置条件,我现在不是在我的生产代码中嵌入测试代码吗,这不是不被接受吗?

2) 使用后置条件是否应该改变我的单元测试的结构?我的第一个想法是断言逻辑从测试转移到后置条件。也就是说,测试将使用相同的输入,我仍在测试我之前测试的所有内容,但现在我不是在单元测试中做出断言,而是对后置条件是否通过进行简单的二进制断言。

3)我的第二个想法是后置条件代码可能具有控制流,因此不适用于测试代码,测试代码应该简单并避免控制流。但是,如果我测试后置条件,我可以在单元测试中依赖它们吗?

4) 测试后置条件似乎很困难,因为如果我正确理解它们,它们基本上通过或失败,您将不得不重复后置条件本身的逻辑以检查它是否做了正确的事情。那么,如何测试后置条件呢?您是否通过不在单元测试中使用它们并确保您的单元测试和后置条件一起通过或失败来检查它们?

5) 我的单元测试有时会验证某个方法是否会导致协作者的状态发生变化。在标准实践中,后置条件是涵盖协作者状态还是仅涵盖它们所定义的类的状态?

4

3 回答 3

5

你在正确的轨道上。

确实,后置条件与单元测试的目的相似。关键区别在于后置条件始终运行,而单元测试仅针对一组已知数据运行。这意味着后置条件不太可能忽略您没有想到的极端情况,但在运行时成本更高。

以下是您的具体问题的答案。

  1. 后置条件存在运行时惩罚。但是(取决于您的环境),可能会为了速度而放弃断言。(在 C 中,您可以使用 #ifdef,在 Java 中查找 AOP,在 Python 中,assert只有在传递 --debug 标志时才能运行任何东西等。)如果您从断言中遇到性能问题,它是可以解决的。然而,我的偏好是让它们一直打开,直到你有理由不这样做。

  2. 您的一些逻辑自然会从单元测试转移到后置条件。但是,值得确保您有单元测试来运行您的后置条件感兴趣的所有案例。如果您为了速度而放弃生产中的断言,则尤其如此。

  3. 后置条件不是单元测试。以任何对他们所做的事情有意义的方式来编写它们。(一般来说,它们应该有点简单。)

  4. 通常,您可以按照 #2 中的描述测试后置条件,方法是传入一组可能违反后置条件的感兴趣的输入,并检查它是否不存在。如果您想测试后置条件本身的逻辑,那么您可以设置可以违反后置条件但只会在测试期间运行的代码。例如,有一个全局变量,测试可以设置它,如果它被设置,用你想要的任何东西替换要返回的数据。现在您可以使后置条件接收您想要的任何输入。

  5. 我不会给你一个硬性规定。它们是你的合同。他们应该说出对功能正在做什么有意义的事情。也就是说,您所描述的可能导致这些对象之间的紧密耦合。紧耦合是你应该有充分理由做的事情。

于 2011-02-20T15:20:30.503 回答
2

合同不是单元测试的一种形式。相反,它们是一种指定(以可执行格式)在调用特定函数或方法之前和之后应该保持哪些条件的方式,并且还可以指定对象的不变量。

当你有合同时,你仍然需要测试,因为你已经指定了函数应该做什么并不意味着它们会真正做到。但是您会发现您的合约将帮助您进行调试 - 因为通过拥有可以检查运行时发生的事情是否符合预期的代码意味着任何逻辑或编程错误都会导致包含错误的代码附近出现故障.

您可能会发现,通过合约,您很乐意进行更少的小型测试和更多的大规模测试,因为即使测试范围很广,合约也可以让您缩小错误来源。此外,单元测试不再需要扮演逻辑应该如何工作的规范的角色,从而进一步限制了较小测试的价值。

合同就像断言一样,您可以选择在生产代码中启用或不启用它们。我的观点是合同往往比断言更昂贵,因此您倾向于在生产中禁用它们。

于 2011-02-20T15:07:20.793 回答
0

与任何方法论或编码风格一样 - 没有单一的正确答案。但是,到目前为止,我发现的一件事是正确的,那就是从来没有“一刀切”的解决方案。

因此,如果您将这些断言实现为设计中每个后置条件的逻辑,我认为这是错误的。

我自己的观点是,只有当未能满足后置条件导致整个系统进入危险的不一致状态时,才应使用此类断言。因此,如果发生类似的事情,我肯定希望系统执行以下操作:向管理员发送电子邮件/短信、停止生产执行、运行诊断或应该为该特定系统执行的任何操作。请注意,这将是一个实际功能,目的是提高安全性,它不是单元测试代码。

另一方面,如果您在每个方法调用之后编写断言,那么您注意到您所做的唯一一件事就是将测试用例硬编码到生产代码中。除了使您的代码库变得一团糟之外,这没有任何实际目的。

于 2011-02-20T15:06:06.573 回答