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

发布于 2024-08-03 18:57:46 字数 862 浏览 6 评论 0原文

例如,这篇文章介绍了它们。

有什么好处?

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

在单元测试主题上 - 鉴于现在的情况,如果您已经练习了自动化测试,那么代码契约肯定没有意义?

更新

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

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

对于单元测试,您仍然必须编写测试以确保不能传递null,并且如果合同中结果大于或等于0是业务逻辑。换句话说,如果我要删除第一个合同,则任何测试都不会中断,除非我专门对此功能进行了测试。然而,这是基于不使用 Visual Studio 更好(终极等)版本中内置的静态分析。

本质上,它们都归结为编写传统 if 语句的另一种方式。我实际使用 TDD 和代码契约 的经验说明了原因,以及我是如何做的。

For example this article introduces them.

What is the benefit?

Static analysis seems cool but at the same time it would prevent the ability to pass null as a parameter in unit test. (if you followed the example in the article that is)

While on the topic of unit testing - given how things are now surely there is no point for code contracts if you already practice automated testing?

Update

Having played with Code Contracts I'm a little disappointed. For example, based on the code in the accepted answer:

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

For unit testing, you still have to write tests to ensure that null cannot be passed, and the result is greater than or equal to zero if the contracts are business logic. In other words, if I was to remove the first contract, no tests would break, unless I had specifically had a test for this feature. This is based on not using the static analysis built into the better (ultimate etc...) editions of Visual Studio however.

Essentially they all boil down to an alternate way of writing traditional if statements. My experience actually using TDD, with Code Contracts shows why, and how I went about it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蓝色星空 2024-08-10 18:57:46

我不认为单元测试和合同相互干扰太多,如果有什么合同应该有助于单元测试,因为它消除了为无效参数添加繁琐的重复测试的需要。契约指定了您可以从函数中期望的最小值,而单元测试则尝试验证特定输入集的实际行为。考虑这个人为的示例:


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

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

I don't think unit testing and contracts interfere with each other that much, and if anything contracts should help unit testing since it removes the need to add tedious repetitive tests for invalid arguments. Contracts specify the minimum you can expect from the function, whereas unit tests attempt to validate the actual behaviour for a particular set of inputs. Consider this contrived example:


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

Clearly the code satisfies the contract, but you'd still need unit testing to validate it actually behaves as you'd expect.

花落人断肠 2024-08-10 18:57:46

有什么好处?

假设您想要确保某个方法永远不会返回 null。现在,通过单元测试,您必须编写一堆测试用例,在其中调用具有不同输入的方法并验证输出不为空。 问题是,您无法测试所有可能的输入。

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

更少的工作,完美的正确性保证。有什么不喜欢的?

What is the benefit?

Let's say that you want to make sure that a method never returns null. Now with unit tests, you have to write a bunch of test cases where you call the method with varying inputs and verify that the output is not null. Trouble is, you can't test all possible inputs.

With code contracts, you just declare that the method never returns null. The static analyzer will then complain if it is not possible to prove that. If it doesn't complain, you know that your assertion is correct for all possible inputs.

Less work, perfect correctness guarantees. What's not to like?

沉默的熊 2024-08-10 18:57:46

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

例如,如果我声明一个整数参数(使用约定符号)在 1 到 10 的范围内,并且我的函数中有一个声明相同大小的本地数组,该数组由参数索引,编译器可以告诉不存在下标错误的可能性,从而产生更好的代码。

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

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

Contracts allow you say what the actual purpose of the code is, as opposed to letting whatever the code does with whatever random arguments are handed it standing as the definition from the point of view of the compiler, or the next reader of the code. This allows significantly better static analysis and code optimization.

For instance, if I declare an integer parameter (using the contract notation) to be in the range of 1 to 10, and I have a local array in my function declared the same size, that is indexed by the parameter, the compiler can tell that there is no possibility of subscript error, thus producing better code.

You can state that null is valid value in a contract.

The purpose of unit testing is to verify dynamically that the code achieves whatever stated purpose it has. Just because you've written a contract for a function, doesn't mean the code does that, or that static analysis can verify the code does that. Unit testing won't go away.

蓝色星空 2024-08-10 18:57:46

一般来说,它不会干扰单元测试。但正如我看到的,您提到了一些关于 TDD 的内容。

如果我从这个角度考虑它,我想它可以/可能会改变标准一个

  • 创建方法(只是签名)
  • 创建单元测试 - >的过程。实现测试
  • 运行测试:让它失败
  • 实现方法,将其破解到最后只是为了使其工作
  • 运行测试:看到它通过
  • 重构你的(可能是混乱的)方法体
  • (重新运行测试只是为了看看你'没有破坏任何东西)

这将是真正困难的全功能单元测试过程。在这样的上下文中,我想您可以在第一点和第二点之间插入代码合同,例如

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

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

编辑

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

Well it will not interfere with unit-testing in general. But as I saw you mentioned something about TDD.

If I think about it from that perspective I guess it could/may change the procedure from the standard one

  • create method (just signature)
  • create Unit test -> implement the test
  • run the test: let it fail
  • implement the method, hack it to the end just to make it working
  • run the test: see it pass
  • refactor your (possibly messy) method body
  • (re-run the test just to see you've not broken anything)

This would be the really hard-full-featured unit-testing procedure. In such a context I guess you could insert code contracts between the 1st and 2nd point like

  • create method (just signature)
  • insert code contracts for the methods input parameters
  • create Unit test -> implement the test
  • ...

The advantage I see at the moment is that you can write easier unit tests in the sense that you wouldn't have to check every possible path since some is already taken into account by your defined contracts. It just gives you additional checking, but it wouldn't replace unit testing since there will always be more logic within the code, more path that have to be tested with unit tests as usual.

Edit

Another possibility I didn't consider before would be to add the code contracts in the refactoring part. Basically as additional way of assuring things. But that would somehow be redundant and since people don't like to do redundant stuff...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文