【问题标题】:What's the best practice in organizing test methods that cover steps of a behavior?组织涵盖行为步骤的测试方法的最佳实践是什么?
【发布时间】:2013-04-09 09:11:39
【问题描述】:

我有一个 FileExtractor 类,其中一个 Start 方法执行一些步骤。

我在名为“FileExtractorTests”的文件夹中创建了一个名为“WhenExtractingInvalidFile.cs”的测试类,并在其中添加了一些测试方法,如下所示,这些方法应作为 Start() 方法的步骤进行验证:

[TestMethod]
public void Should_remove_original_file()
{

}

[TestMethod]
public void Should_add_original_file_to_errorStorage()
{

}

[TestMethod]
public void Should_log_error_locally()
{

}

这样,它可以很好地组织应该满足的行为和期望。

问题是这些测试方法的大部分逻辑都是相同的,所以我应该创建一种测试方法来验证所有步骤还是像上面那样单独创建?

[TestMethod]
public void Should_remove_original_file_then_add_original_file_to_errorStorage_then_log_error_locally()
{      
}

最佳做法是什么?

【问题讨论】:

  • 生产代码是只调用Start()方法还是每一步都调用?
  • 如果您必须同时测试多个步骤 - 那不是集成测试吗?

标签: c# unit-testing mocking tdd mstest


【解决方案1】:

虽然大家普遍认为测试的Act 部分应该只包含一个调用,但对于“每个测试一个断言”的做法仍有很多争论。

我倾向于坚持它,因为:

  • 当测试失败时,我立即(从测试名称)知道我们要在被测方法上验证的多项内容中哪一项出错了。

  • 暗示模拟的测试已经比常规测试更难阅读,当您在同一个测试中针对多个模拟断言时,它们很容易变得晦涩难懂。

如果您不遵循它,我至少建议您包含有意义的 Assert 消息,以便在测试失败时尽量减少头疼。

【讨论】:

    【解决方案2】:

    我会做和你一样的事情,但是使用像这样的_on_start 后缀:Should_remove_original_file_on_start。后一种方法最多只会给您一个断言失败,即使 Start 的所有方面都可能被破坏。

    【讨论】:

    • 但是大部分测试的逻辑会重复吗?一些验证/设置会有所不同(10%),但其他的不会,但 90% 的代码是相同的。我希望我可以创建一个单独的模板方法并只传递参数而不是直接传递。
    • 在测试文件中加入一些辅助方法并没有错。另一种方法可能是将Start 的步骤分成三个方法,您可以从测试中调用它们。它们需要是原子的/自包含的,这样才能在设置方面为您带来很多好处。
    【解决方案3】:

    DRY - 不要重复自己。

    想想你的测试失败的场景。从测试组织的角度来看,什么最有用?

    要查看的第二个维度是维护。您要运行和维护 1 个测试还是 n 个测试?不要因为编写许多没有什么价值的测试而使开发负担过重(我认为出于这个原因,TDD 有点被高估了)。如果测试更长的代码路径而不是短路径,则测试更有价值。

    在这种特殊情况下,我将创建一个测试。如果此测试经常失败并且您没有足够快地找到问题的根本原因,请重新考虑多个测试。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-08
      • 1970-01-01
      • 2012-05-08
      • 2017-11-16
      • 1970-01-01
      • 2010-09-17
      • 2019-03-16
      • 1970-01-01
      相关资源
      最近更新 更多