我正在努力为我正在设计的库/框架编写一组单元测试。对于上下文,请将我的库视为一组相关对象的对象层。
基本上,我正在努力遵守some posts here中有关单元测试的原则和最佳实践,但它们在专门对单元测试库或框架方面似乎存在冲突。 / p>
例如,基本测试涉及“创建工件”。还有一个“删除神器”。但是,由于单元测试应该是独立的并且在完成之后恢复世界状态,所以这些测试看起来都有点相关:在测试工件创建时,我们需要在测试结束时通过实际来清理状态删除它。这意味着工件移除本身是隐式测试的。同样的推理适用于测试工件移除:为了设置世界以便可以测试工件移除,我们需要首先创建一个新工件。
当我们需要对相关子工件的创建和删除进行单元测试时,情况变得更加复杂,我们需要相应地设置世界。
我倾向于按顺序执行一组相关的单元测试,以便每个单元测试都是离散的(即仅测试一件事和一件事),但依赖于序列中的先前测试来逐步设置世界。然后,我的序列可能如下所示:
[create artifact] - > [create sub artifact] - > [remove sub artifact] - > [remove artifact]
有了这个原则,整个库/框架都经过了单元测试,并且在整个测试套件运行结束时恢复了世界状态。但是,这意味着测试套件中间的任何故障都会“打破世界”。
哪些最佳做法和指南可能有助于协调这些相互冲突的需求?
答案 0 :(得分:2)
通过“恢复世界状态”,你的意思是清理一个数据库,或类似的东西?
在这种情况下,您可能已经有一些模拟了持久层的单元测试。这将允许您单独运行单元测试,这不依赖于保持测试之间的状态。
因此,您可以使用其他测试,这听起来更像是“黑盒子”测试,或集成测试,或者您想要调用的任何测试。他们依赖外部状态,必须建立,监控,拆除等。
你绝对应该期望它们比单元测试更脆弱。
我个人认为,一旦你接受了这些测试......这实际上取决于你。你的建议听起来并不合理。
我倾向于构建一套强大的独立单元测试,并依赖于您所描述的最终“胶水测试”测试。因此,它们不是太详细,也不会尝试锻炼图书馆的各个方面。
答案 1 :(得分:1)
良好的单元测试是独立的。运行测试的顺序(或者即使其他测试都在运行)也无关紧要。
为每个测试设置灯具,然后将其拆除(恢复状态)。大多数情况下,如果将变量保持为测试方法的局部变量,则不需要拆卸方法(实现取决于测试框架)。
如果您的图书馆跟踪状态,您可能还有其他问题(例如线程安全)。
答案 2 :(得分:1)
根据Glen Beck和co使用的单元测试的严格定义,在您的情况下创建单元测试很可能实际上是一个坏主意。
对于工作站呼叫范围之外的任何人使用库,你肯定和无可争议地需要的是一整套用户级API系统测试。以与客户端代码相同的方式运行整个系统的那些,而不是孤立的一块一块。
一旦你拥有了那些,那么取决于:
注意:对于作为库的系统,像Junit这样的单元测试工具非常适合创建这样的完整系统测试。因此很多人最终称他们为单元测试。这是好的,只要你不参与给予他们名称改变他们应该工作的方式的论点。
进行良好单元测试的品质不仅不适用,而且可能是有害的。
答案 3 :(得分:0)
不知道你编程的语言是什么。在Java中,您可以使用JUnit API创建TestSuite。您可以将单个子测试添加到TestSuite并作为一个整体运行TestSuite。
答案 4 :(得分:0)
以下是一些有用的最佳做法:
我不同意你的一个假设:测试必须在完成之前恢复其状态。你也可以说测试运行者有责任这样做;像db:unit这样的工具就是这样做的。它们允许您更自由地编写测试。
您的顺序测试是有道理的,但在实践中变得困难。 (我已经尝试过)如你所说,你没有得到关于失败的好报告。此外,随着程序超过4次测试,您需要确定序列中的哪个位置插入测试,以便数据适合它。它很快就会变得难以理解。 (想想100或1000的测试)。这就是测试夹具(或工厂)有意义的原因。
Kent Beck的测试驱动开发:通过示例彻底讨论了这个主题。
答案 5 :(得分:0)
阅读“XUnit测试模式”一书,但阅读Gerard Meszaros(http://xunitpatterns.com/)。它很好地涵盖了这一主题。