TDD:断言的数量,以及实际断言的内容?

时间:2013-09-03 14:41:58

标签: .net tdd nunit moq assert

我目前正在使用TDD编写测试,我遇到了一些问题。

通常在编写单元测试时,我总是习惯于每单元测试使用1个断言,因为这被定义为良好实践,并且很容易理解为什么测试失败。

在TDD中,做同样的事情也是一种好习惯,如果是这样,那么使用TDD有效地设计1种方法我将最终得到超过1次单元测试 - 因为我实际上需要超过1次断言。

另一个问题是我实际上断言了什么?

我可以断言我认为返回对象可能是什么?

所以我必须创建返回类型(可能与许多属性复杂)并确保这些在assert上匹配,这在技术上可能是1断言。

或者另一种方法是确保我沿途编制的模拟实际上被调用,即最小起订我可以执行以下操作

     myServiceMock.Verify(x => x.ItemsReceived(), Times.Once());

所以我可以确保在我的模拟上只调用一个方法,这实际上被归类为断言。所以它回到原始查询,每个单元测试1个断言,所以我需要创建额外的单元测试,以确保调用其他模拟上的其他方法。

其他人在这里做什么?

您是断言在mocks上调用方法还是按照您的期望返回值。

真的很期待任何人对此有任何意见。

2 个答案:

答案 0 :(得分:6)

我认为您的问题有不同的问题,我想介绍一下。

首先,关于每个测试的一个断言。如果这是好的做法,那就有不同的想法。事实是,严格遵守它会使事情变得过于复杂。 TDD应该帮助您设计良好和干净的代码,但不能让您严格遵守某些原则。 您可以在Robert Martin的简洁代码here中阅读有关此主题的内容。

其次,是否要使用模拟框架查看对象内部发生的事情,或者只是断言某些值取决于您正在编写的测试类型。

您可以在验证状态的测试或验证行为的测试中对测试进行分类。

  • 对于基于状态的测试,您将检查与测试相关的某些值,例如:如果我的add函数返回了正确的值。
  • 对于基于行为的测试,您使用模拟对象来验证行为,因为通过生产代码验证结果通常并不容易,例如:想一想如何自动验证直接输出到调制解调器或显示器等设备的数据。

查看Martin Fowler关于此主题的优秀文章Mocks Aren't Stubs

第三,说实话,我并不完全确定你的意思是说:

  

在TDD中,做同样的事情也是一种好习惯,如果是这样,那么使用TDD有效地设计1种方法我将最终得到超过1次单元测试 - 因为我实际上需要超过1次断言。

我认为你认为你只需要编写一个你想要实现的新方法的单一测试,这将是疯狂的。试想实现除法函数。你如何确保除法正常工作,并且在除以0的情况下产生错误?这是不可能的。这意味着您至少需要对此方法进行两次测试。

第四,测试驱动开发中的“测试”是一个非常有偏见的术语。一般来说,人们倾向于认为在TDD中你定义测试以确保你的代码没有被破坏,这通常是测试人员而不是开发人员的任务。但是,在TDD中,您应该考虑单元测试是为尚未实现的方法或类定义行为。

差异似乎微不足道,但这是一个非常有力的陈述。它可以让您在实际编写单行代码之前告诉程序您期望的是什么。想一想,让它沉入其中并尝试一下。它制造了巨大的差异。 BDD是出于这种想法而创建的。但是,还有更多。您可以通过this出色的演示文稿从发明人Dan North那里获得有关BDD的更多详细信息。

所有这些不仅仅是我对事物的看法,也是对优秀软件开发人员的看法。因此,我希望它能为您提供有关TDD的良好视角,并帮助您继续您的旅程,

答案 1 :(得分:6)

这是所有人写测试的常见问题。

在我看来(我知道还有很多其他的)one-assert-per-test-dogma(1A / T)对基本单元测试有效,比如1 + 1 = 2。随着整个应用程序规模的增加,例如集成或系统测试,这将不再起作用。但这可能不是你的范围。

给问题一个面子:

[TestMethod]
public void TwoAsserts()
{
    int a = 42;
    int b = 17;

    int quotient;
    int remainder;

    quotient = Math.DivRem(a, b, out remainder);

    Assert.AreEqual(2, quotient, "quotient is wrong");
    Assert.AreEqual(8, remainder, "remainder is wrong");
}

这两个断言的替代方案是什么?

使用相同的输入但不同的断言进行两次测试? 肯定不会。

编写一个专门的断言方法(就像你提到的那样)检查所有输出并在之后失败? 可能。

但是你应该记住,测试应该易于理解和维护。每个人都在阅读测试 - 包括初学者或不是技术人员 - 应该清楚地看到你要测试的内容以及你的输入,输出和断言是什么。

  

“代码读取的次数比写入的频率高得多”(测试也是如此)

此外,当商称断言失败时,谁在乎你是否有剩余的传递?这是否意味着你通过了50%?不,在质量方面,这将是100%错误。因此,请继续修复您的软件并再次运行测试。 绿色?好!

总结:保持测试简单并尽可能少地使用断言,但需要尽可能多的断言。 阻止你编写测试的一切都是邪恶的。