Answers:
让我首先插入源代码- 使用JUnit用Java进行实用单元测试(也有一个带有C#-Nunit的版本..但是我有这个版本。.在很大程度上与它无关。建议。)
好的测试应该是一个旅行(首字母缩略词不够粘稠-我在书中有一份速查表的打印输出,为了确保我理解正确,我必须将其抽出。)
专业人士:从长远来看,您将拥有与生产一样多的测试代码(如果不是更多的话),因此对于您的测试代码,请遵循相同的良好设计标准。精心设计的方法类-具有意图揭示名称,无重复,具有好名称的测试等。
好的测试也可以快速进行。任何需要半秒以上才能运行的测试都需要进行。测试套件运行的时间越长..运行频率越低。开发人员将尝试在两次运行之间进行更多的更改..如果有任何中断..将花更长的时间来找出哪个更改是罪魁祸首。
更新2010-08:
除了这些以外,其他大多数都是减少低收益工作的准则:例如“不要测试您不拥有的代码”(例如第三方DLL)。不要去测试吸气剂和吸气剂。留意成本效益比或缺陷概率。
这里的大多数答案似乎都是针对一般的单元测试最佳实践(何时,何地,为什么以及什么),而不是实际编写测试(如何)。由于问题似乎在“如何”部分上非常具体,因此我认为应该从我在公司进行的“棕色袋子”演示文稿中发布。
1.使用描述性长的测试方法名称。
- Map_DefaultConstructorShouldCreateEmptyGisMap()
- ShouldAlwaysDelegateXMLCorrectlyToTheCustomHandlers()
- Dog_Object_Should_Eat_Homework_Object_When_Hungry()
2.以“ 编排/动作/断言”风格编写测试。
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.每个测试都必须始终还原其接触的任何资源的状态
牢记这些目标(改编自Meszaros的xUnit Test Patterns)
一些使这更容易的事情:
不要忘记,您也可以使用xUnit框架进行集成测试,但要将集成测试和单元测试分开
出色的单元测试的一些属性:
如果测试失败,应该立即发现问题所在。如果您必须使用调试器来查找问题,则您的测试不够精细。每个测试只有一个断言会有所帮助。
重构时,任何测试都不应失败。
测试应该运行得如此之快,以至于您可以毫不犹豫地运行它们。
所有测试应始终通过;没有不确定的结果。
就像您的生产代码一样,单元测试应精心设计。
@Alotor:如果您建议一个库仅在其外部API上进行单元测试,那么我不同意。我想要每个类的单元测试,包括我不公开给外部调用者的类。(但是,如果我觉得需要为私有方法编写测试,则需要重构。)
编辑:关于“每个测试一个断言”引起的重复的评论。具体来说,如果您有一些代码来设置场景,然后想要对其进行多个声明,但每个测试只有一个声明,则可以在多个测试之间复制设置。
我不采取这种方法。取而代之的是,我针对每种情况使用测试装置。这是一个粗糙的例子:
[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...
}
}
测试最初应该失败。然后,您应该编写使它们通过的代码,否则您将冒编写有漏洞且始终通过的测试的风险。
好的测试需要维护。
我还没有弄清楚如何在复杂的环境中执行此操作。
当您的代码库开始进入成千上万或几百万行代码时,所有教科书开始脱胶。
好的架构可以控制某些交互爆炸,但随着系统变得越来越复杂,不可避免的是自动化测试系统也会随之增长。
这是您必须权衡的地方:
您还需要确定:
您将测试用例存储在代码库中的什么位置?
我可以永远继续下去,但我的意思是:
测试必须是可维护的。
我在《MSDN杂志》的这篇文章中提到了这些原则,我认为这对于任何开发人员来说都是重要的。
我定义“好的”单元测试的方式是,它们是否具有以下三个属性:
杰伊·菲尔德斯(Jay Fields)在编写单元测试方面有很多很好的建议,并且在他的帖子中总结了最重要的建议。您将在此处看到,您应该认真考虑自己的情况,并判断建议是否对您有价值。您可以在此处获得大量令人惊奇的答案,但是由您决定哪个最适合您的情况。尝试一下,如果气味难闻,请进行重构。
亲切的问候
我第二次回答“ A TRIP”,只是测试应该相互依赖!!!
为什么?
干-不要重复自己-也适用于测试!测试依存关系可以帮助1)节省设置时间,2)节省夹具资源,3)查明故障。当然,仅考虑到您的测试框架支持一流的依赖关系。否则,我承认,它们是不好的。
通常,单元测试基于模拟对象或模拟数据。我喜欢编写三种单元测试:
关键是要避免重播所有内容以便能够测试每个功能。
我使用Roy Osherove的单元测试命名标准描述的一致的测试命名约定。给定测试用例类中的每个方法都具有以下命名方式MethodUnderTest_Scenario_ExpectedResult。
每个部分均使用上驼峰式保护套,并由得分下限分隔。
在运行测试时,我发现这很有用,因为测试按测试方法的名称分组。并且有一个约定允许其他开发人员了解测试意图。
如果被测方法已重载,我还将参数附加到方法名称上。