【问题标题】:How to prevent 'over-testing' in a test case? (C#/nUnit)如何防止测试用例中的“过度测试”? (C#/nUnit)
【发布时间】:2010-07-23 09:20:28
【问题描述】:

目前我正在处理一些测试用例,并且我经常发现在每种情况下我都会得到多个断言。例如(为简洁起见,过度简化和删除了 cmets):

[Test]
public void TestNamePropertyCorrectlySetOnInstantiation()
{
  MyClass myInstance = new MyClass("Test name");
  Assert.AreEqual("Test Name", myInstance.Name);
}

这在原则上看起来是可以接受的,但测试的重点是验证当使用给定名称实例化类时,Name 属性设置是否正确,但如果实例化时出现任何错误,它甚至会在它之前失败进入断言。

我是这样重构的:

[Test]
public void TestNamePropertyCorrectlySetOnInstantiation()
{
  MyClass myInstance;
  string namePropertyValue;

  Assert.DoesNotThrow(() => myInstance = new MyClass("Test name"));
  Assert.DoesNotThrow(() => namePropertyValue = myInstance.Name);
  Assert.AreEqual("Test Name", namePropertyValue);
}

当然,现在我实际上在这里测试了三件事;在这个测试中,我对测试 MyClass 的实例是否成功实例化,或者 Name 属性是否被成功读取不感兴趣,这些在另一种情况下进行测试。但是,如果前两个失败,我怎么能测试最后一个断言而不先断言其他两个断言呢?

【问题讨论】:

    标签: c# unit-testing nunit


    【解决方案1】:

    如果您以无效的方式初始化它,则只需进行其他测试来检查是否引发了异常。那时第一种形式很好,IMO。

    就我个人而言,我会避免陷入“每个测试一个断言”的教条。尝试通过代码测试一条逻辑路径,以达到实际意义的细粒度。

    【讨论】:

    • 是的.. 与其说是坚持“每个测试一个断言”,不如说是当它实际测试的代码工作正常时,在测试中看不到红色斑点。
    • @Flynn1179:你为什么会得到一个红色的斑点?我不明白你的意思。
    • 对不起,我忘了说;我正在使用 ReSharper 运行 nUnit 测试,当测试失败时,它在测试列表中显示为红色斑点。在我正在测试的实际断言之前,我有很多测试因为错误而“失败”。
    • @Flynn1179:我的态度是,如果你有 any 测试失败,那很可能会导致其他人失败。如果构造函数总是抛出异常,那么所有需要实例的测试都将不可避免地失败。这就是生活。重要的是应该很容易发现故障并修复它。
    【解决方案2】:

    我真的不明白您所说的过度测试是什么意思,IMO,过度测试就像试图测试私有方法一样。

    我将我的测试作为代码的文档。因此,如果我在一个语句中有多个断言,那么我很有可能将被测方法重构为几个较小的方法,或者有时我将我的测试方法拆分为几个不同的测试方法。遵循每个测试一个断言的规则允许您拥有合理的测试方法名称,这反过来又形成了您的代码文档。我为测试方法遵循的命名约定是 methodName_scenario_expectation(来自 RoyOsherove 的 Art of Unit Testing)。因此,也从代码文档的角度考虑。做,你认为拥有一个断言(除了验证期望之外)会帮助你/其他一些开发人员更好地理解代码,然后继续编写该断言。但是,再次重申,请始终确保您有正确的测试方法名称。

    【讨论】:

    • 通过“过度测试”,我指的是当测试用例“测试”功能与预期不同时,这是设置预期测试的必要结果。
    【解决方案3】:

    在您的特定示例中,您不需要断言某些东西不会抛出,如果它是您的测试执行的一部分。这方面已经在您的第一个测试中进行了测试,更具可读性。如果构造函数或属性 getter 抛出异常,NUnit 将通过适当的错误消息使测试失败。 TBH 我不确定 Assert.DoesNotThrow() 背后的想法是什么,因为如果你省略它,你会得到几乎相同的结果 - 但绝对不应该将它用作正常测试执行的一部分。将异常作为语言的一部分的全部意义在于,您无需在每一行代码之后检查错误。

    【讨论】:

    • 是的,我在想这可能不是最好的例子。虽然一般来说,有些测试显然很常见需要一些特定于该测试的“设置”,但不直接相关到您正在测试的功能。我正在查看Assert.Inconclusive,也许将“设置”包装在一个 try/catch 中,如果失败则断言不确定,但这会使我的代码更不可读。
    【解决方案4】:

    Grzenio 在这点上是正确的。如果抛出异常,第一个最简单的示例测试将失败 - 无需显式测试。

    但是,您应该测试在向其传递无效数据时是否会引发异常

    [Test]
    public void TestInvalidNameThrowsException {
      MyClass myInstance;
      string namePropertyValue;
      Assert.Throws(() => myInstance = new MyClass(null));
      Assert.Throws(() => myInstance = new MyClass("INVALID_NAME_125356232356"));
    }
    

    (例如,我一点 C# 都不懂。但你应该明白。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-05
      • 1970-01-01
      • 1970-01-01
      • 2016-08-12
      相关资源
      最近更新 更多