什么时候可以对类似的单元测试进行分组

本文关键字:单元测试 什么时候 | 更新日期: 2023-09-27 17:56:50

我正在为一个简单的IsBoolean(x)函数编写单元测试来测试值是否为布尔值。我想测试16个不同的值。

如果我不将它们分解为单独的单元测试,并按如下方式将它们一起运行,我是否会在地狱中被烧死,或者被 .NET 编程社区无情地嘲笑(哪个会更糟?):

    [TestMethod]
    public void IsBoolean_VariousValues_ReturnsCorrectly()
    {
        //These should all be considered Boolean values
        Assert.IsTrue(General.IsBoolean(true));
        Assert.IsTrue(General.IsBoolean(false));
        Assert.IsTrue(General.IsBoolean("true"));
        Assert.IsTrue(General.IsBoolean("false"));
        Assert.IsTrue(General.IsBoolean("tRuE"));
        Assert.IsTrue(General.IsBoolean("fAlSe")); 
        Assert.IsTrue(General.IsBoolean(1));
        Assert.IsTrue(General.IsBoolean(0));
        Assert.IsTrue(General.IsBoolean(-1));
        //These should all be considered NOT boolean values
        Assert.IsFalse(General.IsBoolean(null));
        Assert.IsFalse(General.IsBoolean(""));
        Assert.IsFalse(General.IsBoolean("asdf"));
        Assert.IsFalse(General.IsBoolean(DateTime.MaxValue));
        Assert.IsFalse(General.IsBoolean(2));
        Assert.IsFalse(General.IsBoolean(-2));
        Assert.IsFalse(General.IsBoolean(int.MaxValue));
    }

问这个是因为我一直读到的"最佳实践"会要求我做以下事情:

    [TestMethod]
    public void IsBoolean_TrueValue_ReturnsTrue()
    {
        //Arrange
        var value = true;
        //Act
        var returnValue = General.IsBoolean(value);
        //Assert
        Assert.IsTrue(returnValue);
    }
    [TestMethod]
    public void IsBoolean_FalseValue_ReturnsTrue()
    {
        //Arrange
        var value = false;
        //Act
        var returnValue = General.IsBoolean(value);
        //Assert
        Assert.IsTrue(returnValue);
    }
    //Fell asleep at this point

对于 50+ 函数和 500+ 值,我将对此进行测试似乎完全是浪费时间......但这是最佳做法!!!!

-布兰登

什么时候可以对类似的单元测试进行分组

尽管我同意最佳做法是将值分开,以便更轻松地识别错误。 我认为人们仍然必须使用自己的常识,并遵循这样的规则作为指导方针,而不是绝对的。 您希望在单元测试中最小化断言计数,但通常最重要的是确保每个测试具有单个概念

在您的特定情况下,鉴于函数的简单性,我认为您提供的一个单元测试很好。 它易于阅读、简单、清晰。 它还对函数进行了彻底的测试,如果它在某个地方发生故障,您将能够快速识别源代码并对其进行调试。

另外需要注意的是,为了保持良好的单元测试,您需要始终使它们保持最新状态,并像对待实际生产代码一样小心对待它们。 从许多方面来说,这是最大的挑战。 进行测试驱动开发的最佳理由可能是它实际上如何允许您从长远来看更快地编程,因为您不再担心破坏现有的代码。

我不会担心。 这种事情不是重点。 JB Rainsberger在他的演讲中简要谈到了这一点 集成测试是一个骗局. 他说:"如果你从来没有强迫自己每次测试使用一个断言,我建议你尝试一个月。 它将为您提供有关测试的新视角,并教您何时对每个测试有一个断言很重要,何时不重要"。 IMO,这属于无关紧要的类别。

顺便说一下,如果你使用 nunit,你可以使用 TestCaseAttribute,它更好一点:

[TestCase(true)]
[TestCase("tRuE")]
[TestCase(false)]
public void IsBoolean_ValidBoolRepresentations_ReturnsTrue(object candidate)
{
    Assert.That(BooleanService.IsBoolean(candidate), Is.True);
}
[TestCase("-3.14")]
[TestCase("something else")]
[TestCase(7)]
public void IsBoolean_InvalidBoolRepresentations_ReturnsFalse(object candidate)
{
    Assert.That(BooleanService.IsBoolean(candidate), Is.False);
}

编辑:以稍微不同的方式编写测试,我认为更好地传达意图。

最佳做法是将要测试的每个值拆分为单独的单元测试。每个单元测试都应根据要传递的值和预期结果专门命名。如果您正在更改代码并且只破坏了一个测试,那么该测试本身就会失败,而其他 15 个测试将通过。这使您能够立即知道您破坏了什么,而无需调试一个单元测试并找出哪个断言失败。

希望这有帮助。

我不能评论"最佳实践",因为没有这样的事情。

我同意Ayende Rahien在他的博客中所说的话:

最后,它归结为这样一个事实,即我不认为测试是 本身就是产品的价值。他们唯一的价值是他们的 二元能力告诉我产品可以或不可以。 在测试上花费大量额外时间会分散创建真实内容的注意力 价值,可交付的软件。

如果您将它们全部放在一个测试中,并且该测试在"某个地方"失败,那么您该怎么办?要么你的测试框架会准确地告诉你它在哪一行失败,要么,如果失败,你可以使用调试器单步执行它。由于全部在一个功能中,因此所需的额外努力可以忽略不计。

确切地知道在这个特定实例中失败的测试子集的额外价值很小,并且被您必须编写和维护的大量代码所掩盖。

想一想将它们分解为单个测试的原因。这是为了隔离不同的功能,并在测试中断时准确识别所有出错的地方。看起来您可能正在测试两件事:布尔值和非布尔值,因此如果您的代码遵循两条不同的路径,请考虑两个测试。然而,更重要的一点是,如果没有一个测试中断,就没有错误需要查明。

如果您继续运行它们,然后其中一个测试失败,那么是时候将它们重构为单独的测试,并使它们保持这种状态。