单元测试最佳实践之一是使每个测试独立于所有其他测试。假设我想测试BoundedPriorityBlockingQueue自定义类的add()方法:
public void testAdd() {
BoundedPriorityBlockingQueue q = BoundedPriorityBlockingQueue();
q.add(1);
assertEquals(1, q.size());
}
正如您所看到的,当前testAdd使用size()方法,因此它依赖于它,但是当size()被破坏时我不希望testAdd()失败。在这种情况下,最佳做法是什么?
答案 0 :(得分:11)
在这种情况下,最佳做法是什么?
请记住,测试是为了服务你,而不是相反。
如果出现严重错误,你的测试会破裂吗?是。
问题出在哪里?可能是因为使用size
的任何内容都会失败。
这项测试是否会推动您进行不太可测试的设计?否。
这是测试add
的最简单方法吗?面对不断变化的实施细节,这是一种强大的方法吗?大概。 (我想测试你可以再次获得价值,请注意。)
是的,它有点测试同一类的两个部分 - 但我真的不认为这是一个问题。我看到很多关于测试的教条(“只测试公共API,总是使用AAA”等) - 根据我的经验,你应该用健康的实用主义来缓和教条主义。
答案 1 :(得分:6)
目标是使所有测试方法独立于其他 test 方法,并且此方法是独立的。无论你在其他测试方法中做什么,它都会根据被测试类中方法的操作而通过或失败。
如果测试中的其他方法被破坏,则此测试失败是正常的。如果size()
被破坏,您将有多个测试失败(这一个和明确测试size()
的失败),因此问题显而易见。如果add()
被破坏,则只有此测试失败(以及依赖add()
)的任何其他方法。
答案 2 :(得分:2)
正如其他人已经说过的那样,如果你的size
方法被破坏,那么测试就会失败,所以你有理由去调查并理解为什么会发生这种情况。
无论如何,如果您仍然对测试之间具有这种独立性感兴趣,那么您可以选择白盒测试策略:我猜你的BoundedPropertyBlockingQueue
内部使用任何一个java.util
集合,您依赖的其他提供程序(Guava,Apache Collections等)的数组或集合实现,因此您无需验证这些结构是否按预期工作。
因此,将内部结构定义为protected
,将测试类放在具有相同名称的包中,而不是依赖于size
方法的实现,进入BoundedPropertyBlockingQueue
:
BoundedPriorityBlockingQueue q = BoundedPriorityBlockingQueue();
q.add(1);
assertEquals(1, q.contents.size()); // assuming that `contents` attribute is a collection.
主要缺点是,如果您的队列内部实现发生变化,您需要更改测试,同时使用之前的测试方法,您将不需要。
IMO我会选择你当前的实现,更少耦合,最后,实现其目标。
答案 3 :(得分:1)
进行这样的交叉测试没有任何问题 - 有些方法倾向于成对生成(添加/删除,入队/出队等),没有其补充部分测试一个方法没有意义
但是,我会更多地考虑一下客户端(类用户)将如何使用add
方法。最有可能不会调用add来确定是否更改了大小,而是稍后检索添加的项目。也许你的测试看起来应该更像这样:
BoundedPriorityBlockingQueue q = new BoundedPriorityBlockingQueue();
QueueItem toAdd = 1;
QueueItem added = q.dequeue();
assertEquals(toAdded, added);
除此之外,您还可以在上面的测试中添加guard assert(以确保队列不会从已添加的某些项开始)或更好 - 包括保证初始状态的单独测试队列的em>(大小为0,出队返回null / throw)。