我正在为一个简单的IsBoolean(x)函数编写单元测试来测试一个值是否为boolean。我想测试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多个值,我将对此进行测试似乎完全浪费时间......但这是最佳实践!!!!!
-Brendan
答案 0 :(得分:3)
我不担心。这种事情不是重点。 JB Rainsberger在他的演讲Integration Tests are a Scam中简要地谈到了这一点。他说过,“如果你从未强迫自己每次测试使用一个断言,我建议你试用一个月。它会给你一个新的测试视角,并告诉你什么时候每个测试有一个断言,当它没有“。 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);
}
编辑:以稍微不同的方式编写测试,我认为传达意图更好。
答案 1 :(得分:2)
虽然我同意最佳做法是将值分开,以便更容易识别错误。我认为仍然必须使用他们自己的常识并遵循指南这样的规则,而不是绝对的。您希望在单元测试中最小化断言计数,但通常最重要的是确保每次测试单一概念。
在您的具体情况下,考虑到功能的简单性,我认为您提供的单元测试很好。它易于阅读,简单明了。它还彻底测试了这个功能,如果它在某个地方突破,你就可以快速识别它并调试它。
另外需要注意的是,为了保持良好的单元测试,您需要始终保持最新状态,并按照与实际生产代码相同的方式对待它们。这在很多方面都是最大的挑战。测试驱动开发的最佳理由可能是它实际上允许您从长远来看编程更快,因为您不必担心破坏存在的代码。
答案 2 :(得分:0)
最佳做法是将要测试的每个值拆分为单独的单元测试。每个单元测试应专门命名为您传递的值和预期结果。如果您正在更改代码并且只打破了一个测试,那么单独的测试将失败而另外15个测试将通过。这样你就可以立即知道你的内容,而无需调试一个单元测试并找出哪个断言失败。
希望这有帮助。
答案 3 :(得分:0)
我无法对“最佳实践”发表评论,因为there is no such thing。
我同意Ayende Rahien所说的in his blog:
最后,它归结为我不考虑测试的事实 本身就是产品的价值。他们唯一的价值就是他们 二进制能力告诉我产品是否正常。 在测试上花费大量额外时间会分散创建真实性的注意力 价值,可运送的软件。
如果你把它们全部放在一个测试中并且这个测试在某个地方失败了,那么你做什么?您的测试框架将告诉您它确实在哪条线路上失败,或者,如果失败,您可以使用调试器逐步完成它。需要额外的努力,因为它在一个函数中都可以忽略不计。
确切地知道在这个特定实例中哪个测试子集失败的额外值很小,并且由于你必须编写和维护的大量代码而蒙上了阴影。
答案 4 :(得分:0)
想一下将它们分解为单独测试的原因。这是为了隔离不同的功能,并准确识别测试中断时出错的所有事情。看起来您可能正在测试两件事:布尔值和非布尔值,因此如果您的代码遵循两个不同的路径,请考虑两个测试。但更重要的是,如果没有任何测试中断,则没有错误可以确定。
如果你继续运行它们,后来其中一个测试失败了,那就是将它们重构为单独测试的时候了,并让它们保持这种状态。