我在某处读到每个测试必须只测试一件事.但是,在良好实践手册中允许对类似行为进行分组吗?我目前正在编写一些测试(C#with NUnit),而bellow是我所面临的一个例子:
[TearDown]
public void Cleanup()
{
Hotkeys.UnregisterAllLocals();
Hotkeys.UnregisterAllGlobals();
}
[Test]
public void KeyOrderDoesNotMatter()
{
Hotkeys.RegisterGlobal("Ctrl+Alt+P", delegate { });
Assert.That(Hotkeys.IsRegisteredGlobal("Alt+P+Ctrl"), Is.True);
}
[Test]
public void KeyCaseDoesNotMatter()
{
Hotkeys.RegisterGlobal("Ctrl+Alt+P", delegate { });
Assert.That(Hotkeys.IsRegisteredGlobal("ctrl+alt+p"), Is.True);
}
[Test]
public void KeySpacesDoesNotMatter()
{
Hotkeys.RegisterGlobal("Ctrl+Alt+P", delegate { });
Assert.That(Hotkeys.IsRegisteredGlobal("Ctrl + Alt + P"), Is.True);
}
Run Code Online (Sandbox Code Playgroud)
分组后,他们将成为:
[TearDown]
public void Cleanup()
{
Hotkeys.UnregisterAllLocals();
Hotkeys.UnregisterAllGlobals();
}
[Test]
public void KeyIsNotStrict()
{
// order
Hotkeys.RegisterGlobal("Ctrl+Alt+A", delegate { });
Assert.That(Hotkeys.IsRegisteredGlobal("Alt+A+Ctrl"), Is.True);
// whitespace
Hotkeys.RegisterGlobal("Ctrl+Alt+B", delegate { });
Assert.That(Hotkeys.IsRegisteredGlobal("Ctrl + Alt + B"), Is.True);
// case
Hotkeys.RegisterGlobal("Ctrl+Alt+C", delegate { });
Assert.That(Hotkeys.IsRegisteredGlobal("ctrl+alt+c"), Is.True);
}
Run Code Online (Sandbox Code Playgroud)
那么什么是最佳实践(如果存在)以及为什么?
obs:我对单元测试比较新...
最简洁的答案是不.您应该尽可能简化测试.每个测试应该只测试一件事.
有一种用于单元测试的Arrange-Act-Assert(AAA)模式.这表明你应该在测试方法(Arrange)的开头做一些准备工作,然后对这个测试进行操作检查,并在方法结束时做一些断言.
另外,我建议您阅读有关单元测试的FIRST模式.
UPD:
当你使测试变得复杂时 - 当测试变为"红色"时很难识别出什么是错误的,你知道某些断言失败了,但你必须阅读日志以了解哪一个.此外 - 如果复杂测试中的第一个断言失败,您甚至不知道其余断言是否正常.维护大型单元测试也很困难,你为第一个断言做的一些工作可能会产生影响下一个断言的副作用.
但是你应该考虑到你对GRASP的测试也应该是低耦合/高内聚,因此,正如@Schwern在他的回答中提到的,你不应该编写单独的测试,只是为了最小化断言,如果不同的测试将结束测试相同的逻辑事物.开发人员始终可以自行决定在每种特定情况下哪种方式正确.
注意:我不是C#程序员.
一方面,你"在测试中只做一件事".另一方面,你有干燥原则.你被问到要违反哪一个.这取决于你违反每一项的程度,你从违规中获得多少利益,以及为什么这些规则首先存在.
您的分组解决方案并不理想,因为它仍然会重复.如果相反你做了这个......
[TearDown]
public void Cleanup()
{
Hotkeys.UnregisterAllLocals();
Hotkeys.UnregisterAllGlobals();
}
[Test]
public void IsRegisteredGlobal_InputNormalization()
{
Hotkeys.RegisterGlobal("Ctrl+Alt+P", delegate { });
Assert.IsTrue(Hotkeys.IsRegisteredGlobal("Alt+P+Ctrl"), "order independent");
Assert.IsTrue(Hotkeys.IsRegisteredGlobal("ctrl+alt+p"), "case insensitive");
Assert.IsTrue(Hotkeys.IsRegisteredGlobal("Ctrl + Alt + P"), "whitespace independent");
}
Run Code Online (Sandbox Code Playgroud)
然后你就不会违反DRY,你几乎不会"在测试中只做一件事".它仍在做一件"事情",那件事正在规范IsRegisteredGlobal的输入.
每次测试只做一件事就是为了隔离它们.这使得测试更容易隔离和调试.这并不意味着每次测试都有一个断言.上面的测试仍然只做一件事,但它正在以三种非常非常相似的方式测试它.还行吧.以前,您的断言是由测试名称解释的.现在通过与每个断言相关联的消息来解释它们.失败的原因仍然很明显,我在第一.
此外,如果您使用每个方法使用一个断言编写所有测试并且不必要地一遍又一遍地重复相同的代码,则不仅违反DRY,而且重复的代码可能开始减慢违反第一个F的情况.
如果你改变了他们的订单,检查Hotkeys.IsRegisteredGlobal("Alt+P+Ctrl")可能会Hotkeys.IsRegisteredGlobal("ctrl+alt+p")变成真的吗?是.但是在隔离,速度和便利之间总是存在折衷.如果您怀疑可能会干扰另一个人,则应将其隔离.我要说的是,如果你想检查之间没有耦合那么你应该在自己的测试中明确地做到这一点,而不是让每个测试都背负这个负担.
是的,运行代码并在其上执行多个断言很好,但始终记住它是良好代码和良好测试之间的平衡行为.通常测试会获胜,但不要对此感到愚蠢.
我把它切换到IsTrue了通用的That,而不是通用的紧凑性,清晰度和可能更好的故障诊断. Assert.That( thing, condition )意味着你直到最后都不知道你要测试的是什么.你必须阅读整行,看看条件是什么,并根据你真正测试的内容再次阅读整行. Assert.IsTrue预先告诉你. Assert.That在复杂的断言中可能更具可读性,但在简单的断言中它的可读性较差.可以适当地使用它们.(注意:Perl程序员)
它还可以产生更好的故障诊断,因为您可以向NUnit提供有关您的意图的更多信息.它不是"匹配那个",而是"这是真的",因此它可以产生更精确和精心设计的断言.虽然NUnit也可能足够聪明Is.True,无论如何都能看到你正在测试并做到这一点.它没有伤害.