.NET 4.0代码契约 - 它们将如何影响单元测试?

时间:2009-09-05 15:02:57

标签: unit-testing .net-4.0 code-contracts microsoft-contracts

例如,这个article介绍了它们。

有什么好处?

静态分析似乎很酷但同时它会阻止在单元测试中将null作为参数传递的能力。 (如果您按照文章中的示例进行操作)

关于单元测试的主题 - 如果您已经实施自动化测试,那么现在确定代码合同没有意义吗?

更新

玩过代码合约后,我有点失望。例如,根据接受的答案中的代码:

public double CalculateTotal(Order order)
{
    Contract.Requires(order != null);
    Contract.Ensures(Contract.Result<double>() >= 0);
    return 2.0;
}

对于单元测试,仍然必须编写测试以确保无法传递null,并且如果合同是业务逻辑。换句话说,如果我要删除第一个合同,除非我特意测试了这个功能,否则没有测试会中断。这是基于不使用内置于Visual Studio的更好(终极等)版本中的静态分析。

基本上,他们都归结为编写传统if语句的另一种方式。我实际使用TDD, with Code Contracts的经验说明了原因,以及我如何使用它。

4 个答案:

答案 0 :(得分:38)

我不认为单元测试和合同会相互干扰那么多,如果有任何合同应该帮助单元测试,因为它不需要为无效参数添加繁琐的重复测试。合同指定了函数的最小值,而单元测试则尝试验证特定输入集的实际行为。考虑这个人为的例子:


public class Order
{
    public IEnumerable Items { get; }
}

public class OrderCalculator
{
    public double CalculateTotal(Order order)
    {
        Contract.Requires(order != null);
        Contract.Ensures(Contract.Result<double>() >= 0);

        return 2.0;
    }
}

显然,代码符合合同,但您仍然需要单元测试来验证它实际上是否符合您的预期。

答案 1 :(得分:27)

  

有什么好处?

假设您要确保方法永远不会返回null。现在使用单元测试,您必须编写一组测试用例,您可以使用不同的输入调用方法,并验证输出不为null。 麻烦的是,您无法测试所有可能的输入。

使用代码契约,您只需声明该方法永远不会返回null。如果无法证明这一点,静态分析仪会抱怨。如果没有抱怨,您知道您的断言对于所有可能的输入都是正确的

减少工作量,完美正确保证。什么不喜欢?

答案 2 :(得分:4)

契约允许你说出代码的实际目的是什么,而不是让代码用任何随机参数做什么,从编译器或者下一个读者的角度来看它作为定义。码。这样可以显着改善静态分析和代码优化。

例如,如果我声明一个整数参数(使用合约表示法)在1到10的范围内,并且我在我的函数中声明了相同大小的本地数组,该参数由参数索引,编译器可以判断出没有下标错误的可能性,从而产生更好的代码。

您可以声明null是合同中的有效值。

单元测试的目的是验证动态地代码实现其具有的任何声明目的。仅仅因为您已经为函数编写了一个契约,并不意味着代码会这样做,或者静态分析可以验证代码是否这样做。单元测试不会消失。

答案 3 :(得分:3)

一般来说,它不会干扰单元测试。但正如我所见,你提到了一些关于TDD的事情。

如果我从这个角度思考它,我想它可能/可能会改变标准的程序

  • 创建方法(只是签名)
  • 创建单元测试 - &gt;实施测试
  • 运行测试:让它失败
  • 实施该方法,将其破解为最终只是为了使其正常工作
  • 运行测试:看到它通过
  • 重构你的(可能是凌乱的)方法主体
  • (重新进行测试只是为了看你没有破坏任何东西)

这将是真正功能齐全的单元测试程序。在这样的上下文中,我猜你可以在第一点和第二点之间插入代码契约,如

  • 创建方法(只是签名)
  • 插入方法输入参数的代码合约
  • 创建单元测试 - &gt;实施测试
  • ...

我目前看到的优势是,您可以编写更简单的单元测试,因为您不必检查每条可能的路径,因为已定义的合同已经考虑了某些路径。它只是给你额外的检查,但它不会取代单元测试,因为代码中总会有更多的逻辑,更多的路径必须像往常一样用单元测试进行测试。

修改

我之前没有考虑的另一种可能性是在重构部分添加代码契约。基本上作为确保事情的额外方式。但这在某种程度上是多余的,因为人们不喜欢做多余的事情......