如何使用代码合约进行单元测试

12

如何在 .NET 4.0 Code Contracts 中实践 TDD 最佳建议?

我认为具体来说,我的问题是,考虑到 TDD 的一个目的是让代码自我记录,并且合同现在提供了部分记录,那么代码合同是否应该像其他代码一样进行测试?

5个回答

11

这取决于您如何使用合同以及您正在开发什么类型的应用程序。

首先:您肯定不希望单独测试断言和后置条件(Contract.AssertContract.AssumeContract.EnsuresContract.EnsuresOnThrow)。 我认为这样做没有实际价值-由于重写器已经在运行时验证它们,即使没有测试,您也会很快发现失败。
然而,在经过充分测试的应用程序中,没有后置条件或断言应该失败-即使是在无效输入的情况下。因此,如果所有测试(甚至是测试处理无效数据的测试!)都通过而没有一个后置条件/断言失败,则可以将其视为“已测试”的后置条件和断言。
为此,您可能需要使用“Assert.Fail”在测试中处理ContractFailed事件。

现在有趣的部分是前置条件:
您正在开发库吗?然后,如果您的时间/预算允许,您绝对应该测试它们(不测试实际逻辑更糟)。
特别是,如果您使用“Contract.Requires<E>”重载,在合同失败时会抛出特定异常,则应像常规参数验证一样使用“if-throw”构造来测试它们。

如果您不是编写库,则我不认为测试前置条件真的是必要的-它们不是真正的业务要求,而只是调试助手。
而且,为每个方法应该抛出ArgumentNullException的参数进行单元测试可能会变得非常无聊。
如果您在方法中忘记此验证代码(即:特定的Contract.Requires),则您可能也会忘记单元测试。因此,参数验证测试为您的(非库)代码带来的附加价值与相关价值相比非常低。

总之:不要测试后置条件和断言。确实测试前置条件-但仅针对库(以及可能像库一样使用的代码部分)。


“不要测试后置条件和断言。确保测试前置条件 - 但仅限于库(以及可能像库一样使用的代码部分)。” - 这听起来像是相当不错的建议:这是基于您自己的经验还是基于接受的智慧? - satnhak
@B Tyler:这是基于我的经验和与其他程序员的讨论。然而,这仅仅是我的个人观点,并没有任何研究或长期研究来支持它;-) - Matthias
嗯,我认为你应该测试所有的前置条件,因为你可能会在前置条件逻辑本身中犯错误(例如,打错数字或字符串)。唯一知道你没有以这种方式搞砸的方法就是测试它。但除此之外,我同意你关于不需要测试后置条件和断言的观点。 - fourpastmidnight

4

我不同意其他人的观点。合同并不是测试,它们是关于API需求和承诺的断言。它们并不能神奇地证明你的代码是正确的,只有在你违反合同时,它们才会在运行时提供信息。我不知道你们怎么想,但我不希望发布的代码在某些边角情况下未能遵守合同而崩溃!与任何其他行为一样,合同应该进行单元测试。如果你不执行合同(以及间接执行合同的代码路径),那么你就没有证据证明代码的有效性。代码合同和单元测试并不是互斥的概念。


1
它们不仅仅是运行时信息;还可以进行静态验证,导致编译时错误(例如当带有可以为空的参数的方法被调用时,但契约要求它不为空)。 - TheXenocide

1

好问题。简单的答案是否定的。代码合同可以处理与系统行为无关的多余测试。如果您真的可以达到100%的代码覆盖率,您需要处理isnull检查等。这些检查不需要在您的测试套件中。额外的好处是这些将在编译时进行检查,而不是等待测试执行。

希望这可以帮助您。


1
一个与代码合同结合使用的有用工具是Pex,它分析您的代码并为其生成基本单元测试。最棒的是,它识别和理解代码合同,因此可以调整生成的测试代码。
如果您拥有MSDN订阅,则可以将Pex/Moles作为PowerTool下载,否则您可以在http://research.microsoft.com/en-us/projects/pex/downloads.aspx下载它(不是最新版本)。

1

测试旨在测试代码是否按预期运行。

您不应明确编写测试以执行合同断言。

但是,在TDD或进行代码更改时,运行单元测试可能会以导致合同失败的方式运行代码 - 当发生这种情况时,测试应该失败,并且您需要能够快速轻松地找到失败的合同,以便您可以更正代码。

因此,您希望捕获ContractException,即使只是为了执行Asser.Fail(“未满足合同要求”)

这可能更符合您的需求 如何在使用代码合同时记录错误


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