我相信你们大多数人都在编写大量自动化测试,并且在单元测试时也遇到了一些常见的陷阱。
我的问题是你是否遵循任何编写测试的行为准则以避免将来出现问题?更具体一点:良好单元测试的属性是什么?或者您如何编写测试?
鼓励语言不可知的建议。
答案 0 :(得分:93)
让我先来看一下插件来源 - Pragmatic Unit Testing in Java with JUnit(还有一个带有C#-Nunit的版本......但是我有这个...它的大部分都是不可知的。推荐。)
好的测试应该一个TRIP (这个首字母缩略词不够粘 - 我在书中打印了一张cheatsheet,我必须拔出来确保我做对了.. )
专业:从长远来看,您将拥有与生产一样多的测试代码(如果不是更多),因此您的测试代码遵循相同的良好设计标准。精心设计的方法 - 具有意图揭示名称的类,无重复,具有良好名称的测试等。
好的测试也可以快速。任何需要超过半秒才能运行的测试......需要加以研究。测试套件运行所需的时间越长,运行的频率就越低。开发人员试图在两次运行之间潜行的变化越多......如果有什么破坏......需要更长的时间来确定哪个变化是罪魁祸首。
2010-08更新:
除此之外,其他大部分都是削减低效益工作的指导方针:例如: '不要测试你不拥有的代码'(例如第三方DLL)。不要去测试getter和setter。密切关注成本效益比或缺陷概率。
答案 1 :(得分:42)
答案 2 :(得分:41)
这里的大部分答案似乎都是针对单元测试的最佳实践(何时,何地,为什么以及什么),而不是实际编写测试本身(如何)。由于这个问题在“如何”部分看起来非常具体,我想我会发布这个,取自我在公司进行的“棕色包”演示。
<强> 1。使用长的描述性测试方法名称。
- Map_DefaultConstructorShouldCreateEmptyGisMap()
- ShouldAlwaysDelegateXMLCorrectlyToTheCustomHandlers()
- Dog_Object_Should_Eat_Homework_Object_When_Hungry()
<强> 2。用Arrange/Act/Assert style
编写测试第3。始终使用您的断言提供失败消息。
Assert.That(x == 2 && y == 2, "An incorrect number of begin/end element
processing events was raised by the XElementSerializer");
<强> 4。评论测试的原因 - 什么是业务假设?
/// A layer cannot be constructed with a null gisLayer, as every function
/// in the Layer class assumes that a valid gisLayer is present.
[Test]
public void ShouldNotAllowConstructionWithANullGisLayer()
{
}
<强> 5。每次测试都必须始终恢复其接触的任何资源的状态
答案 3 :(得分:17)
牢记这些目标(改编自Meszaros的xUnit Test Patterns一书)
使这更容易的一些事情:
不要忘记您也可以使用xUnit框架进行集成测试但是将集成测试与单元测试分开
答案 4 :(得分:9)
优秀的单元测试的一些属性:
当测试失败时,应立即明白问题所在。如果必须使用调试器来追踪问题,那么您的测试就不够精确。每次测试只有一个断言有帮助。
重构时,任何测试都不会失败。
测试应该运行得如此之快,以至于您可以毫不犹豫地运行它们。
所有测试都应该通过;没有非确定性的结果。
单元测试应该是一个很好的因素,就像您的生产代码一样。
@Alotor:如果您建议库只应在其外部API上进行单元测试,我不同意。我想要为每个类进行单元测试,包括我不向外部调用者公开的类。 (但是,if I feel the need to write tests for private methods, then I need to refactor.)
我不接受这种方法。相反,我在每个场景中使用测试夹具 。这是一个粗略的例子:
[TestFixture]
public class StackTests
{
[TestFixture]
public class EmptyTests
{
Stack<int> _stack;
[TestSetup]
public void TestSetup()
{
_stack = new Stack<int>();
}
[TestMethod]
[ExpectedException (typeof(Exception))]
public void PopFails()
{
_stack.Pop();
}
[TestMethod]
public void IsEmpty()
{
Assert(_stack.IsEmpty());
}
}
[TestFixture]
public class PushedOneTests
{
Stack<int> _stack;
[TestSetup]
public void TestSetup()
{
_stack = new Stack<int>();
_stack.Push(7);
}
// Tests for one item on the stack...
}
}
答案 5 :(得分:9)
应该隔离测试。一项测试不应该依赖于另一项测试。更进一步,测试不应该依赖外部系统。换句话说,测试您的代码,而不是您的代码所依赖的代码。您可以将这些交互作为集成或功能测试的一部分进行测试。
答案 6 :(得分:7)
你所追求的是被测试阶级行为的界定。
基本意图是增加你对班级行为的信心。
在查看重构代码时,这非常有用。 Martin Fowler在他的网站上有一个关于测试的有趣article。
HTH。
欢呼声,
罗布
答案 7 :(得分:7)
测试应该最初失败。然后你应该编写使它们通过的代码,否则你冒着编写错误并且总是通过的测试的风险。
答案 8 :(得分:6)
我喜欢上述Pragmatic Unit Testing书中的正确BICEP首字母缩略词:
就我个人而言,我觉得你可以通过检查你得到正确的结果(1 + 1应该在一个附加函数中返回2)得到很好的结果,尝试你能想到的所有边界条件(例如使用两个数字其总和大于add函数中的整数最大值)并强制出现网络故障等错误情况。
答案 9 :(得分:6)
良好的测试需要维护。
我还没有弄清楚如何在复杂环境中这样做。
随着您的代码库开始到达,所有教科书都开始脱口而出 进入数百个1000或数百行代码。
良好的架构可以控制一些交互爆炸,但不可避免地如此 随着自动化测试系统的发展,系统变得越来越复杂。
这是您开始处理权衡的地方:
您还需要决定:
您在哪里将测试用例存储在代码库中?
我可以永远继续下去,但我的观点是:
测试需要维护。
答案 10 :(得分:5)
我在This MSDN Magazine article中讨论了这些原则,我认为这对任何开发人员都很重要。
我定义“好”单元测试的方式是,它们是否具有以下三个属性:
答案 11 :(得分:4)
答案 12 :(得分:2)
Jay Fields有关于写作单元测试的lot of good advices,并且有a post where he summarize the most important advices。在那里你会读到你应该批判性地考虑你的背景,并判断这些建议是否值得你。你在这里得到了大量惊人的答案,但由你自己决定哪种方式最适合你的背景。试试它们,如果它闻起来不好就重构一下。
亲切的问候
答案 13 :(得分:1)
永远不要假设一个简单的2行方法可行。编写快速单元测试是防止丢失空测试,错位减号和/或微小范围错误咬你的唯一方法,不可避免地,当你处理它的时间比现在更少时。
答案 14 :(得分:1)
我的第二个“A TRIP”答案,除了测试应该相互依赖!!!
为什么?
DRY - 不要重复自己 - 也适用于测试!测试依赖性可以帮助1)节省设置时间,2)节省夹具资源,以及3)精确定位到故障。当然,只有你的测试框架支持一流的依赖。否则,我承认,他们很糟糕。
答案 15 :(得分:0)
我使用Roy Osherove's Unit Test Naming standards描述的一致测试命名约定。给定测试用例类中的每个方法都具有以下命名样式MethodUnderTest_Scenario_ExpectedResult。
每个部分都使用Upper Camel Case,并以低分为界。
我在运行测试时发现这很有用,测试按测试方法的名称分组。并且有一个约定允许其他开发人员理解测试意图。
如果测试中的方法已经过载,我还会将参数附加到方法名称。
答案 16 :(得分:0)
考虑两种类型的测试,并以不同方式对待它们 - 功能测试和性能测试。
为每个输入使用不同的输入和指标。您可能需要为每种类型的测试使用不同的软件。
答案 17 :(得分:0)
单元测试通常基于模拟对象或模拟数据。 我喜欢写三种单元测试:
重点是避免重播所有以便能够测试每个功能。