代码之家  ›  专栏  ›  技术社区  ›  Finglas

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

  •  37
  • Finglas  · 技术社区  · 15 年前

    例如: article 介绍他们。

    有什么好处?

    静态分析看起来很酷,但同时它会阻止在单元测试中将空值作为参数传递。(如果您遵循本文中的示例,即

    在单元测试这个话题上,考虑到现在的情况,如果您已经实践了自动化测试,那么代码契约就没有意义了。

    更新

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

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

    对于单元测试,您 仍然 必须编写测试以确保不能传递空值,如果合同为 业务逻辑 . 换句话说,如果我要删除第一个契约,任何测试都不会中断,除非我专门为此特性进行了测试。但是,这是基于不使用嵌入到Visual Studio的更好(终极等)版本中的静态分析。

    从本质上讲,它们都归结为书写传统if语句的另一种方式。我实际使用的经验 TDD, with Code Contracts 说明为什么,以及我是怎么做的。

    4 回复  |  直到 10 年前
        1
  •  38
  •   Lee    15 年前

    我不认为单元测试和契约会互相干扰那么多,如果有什么契约可以帮助单元测试,因为它消除了为无效参数添加冗长的重复测试的需要。契约指定了您可以从函数期望的最小值,而单元测试则试图验证特定输入集的实际行为。考虑这个人为的例子:

    
    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;
        }
    }
    

    显然,代码满足契约,但是您仍然需要单元测试来验证它的实际行为是否如您所期望的那样。

        2
  •  27
  •   Wim Coenen    15 年前

    有什么好处?

    假设您希望确保某个方法永远不会返回 null .现在,对于单元测试,您必须编写一组测试案例,在这些案例中,您使用不同的输入调用方法,并验证输出是否为空。 问题是,您不能测试所有可能的输入。

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

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

        3
  •  4
  •   Ira Baxter    10 年前

    契约允许您说出代码的实际用途,而不是让任何随机参数处理的代码从编译器或下一个代码阅读器的角度作为定义。这使得静态分析和代码优化明显更好。

    例如,如果我声明一个整型参数(使用约定表示法)在1到10的范围内,并且我的函数中有一个局部数组声明了相同的大小,它是由参数索引的,那么编译器就可以判断出不存在下标错误的可能性,从而生成更好的代码。

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

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

        4
  •  3
  •   Robert Koritnik    11 年前

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

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

    • 创建方法(仅签名)
    • 创建单元测试->实现测试
    • 运行测试:让它失败
    • 实现这个方法,把它黑客到底,让它工作。
    • 运行测试:看到它通过
    • 重构(可能是混乱的)方法体
    • (重新运行测试只是为了看你没有破坏任何东西)

    这将是一个非常困难的、功能齐全的单元测试过程。在这种情况下,我想你可以在第一点和第二点之间插入代码契约,比如

    • 创建方法(仅签名)
    • 为方法输入参数插入代码协定
    • 创建单元测试->实现测试

    我现在看到的优点是,您可以编写更简单的单元测试,因为您定义的契约已经考虑了一些可能的路径,所以您不必检查每个可能的路径。它只是提供了额外的检查,但是它不会取代单元测试,因为代码中总是有更多的逻辑,更多的路径必须像往常一样用单元测试进行测试。

    编辑

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