5

单元测试最佳实践之一是使每个测试独立于所有其他测试。假设我想测试 BoundedPriorityBlockingQueue 自定义类的 add() 方法:

public void testAdd() {
    BoundedPriorityBlockingQueue q = BoundedPriorityBlockingQueue();
    q.add(1);
    assertEquals(1, q.size());
}

正如您所看到的,目前 testAdd 使用 size() 方法,因此它取决于它,但我不希望 testAdd() 在 size() 被破坏时失败。在这种情况下,最佳做法是什么?

4

4 回答 4

12

在这种情况下,最佳做法是什么?

只是接受它,记住测试是为你服务的,而不是反过来。

如果出现可怕的错误,您的测试会中断吗?是的。

问题出在哪里会很清楚吗?可能,考虑到任何使用size都会失败。

这个测试是否会驱使您采用可测试性较低的设计?不。

这是最简单的测试方法add,面对不断变化的实现细节,它是健壮的吗?大概。(请注意,我会测试您是否可以再次获得价值。)

是的,这有点像测试同一类的两个部分——但我真的不认为这是个问题。我看到很多关于测试的教条(“只测试公共 API,总是使用 AAA”等)——根据我的经验,你应该用健康的实用主义来缓和这种教条主义。

于 2013-09-26T12:44:27.240 回答
6

目标是让所有测试方法独立于其他测试方法,而这个方法是独立的。无论您在其他测试方法中做什么,它都会根据被测类中方法的操作通过或失败。

如果被测类的另一个方法被破坏,这个测试失败也没关系。如果size()损坏,您将有多次测试失败(这一次和明确测试的一次size()),所以问题出在哪里很明显。如果add()被破坏,只有这个测试会失败(以及任何其他依赖的方法add())。

于 2013-09-26T12:44:04.530 回答
2

正如其他人已经说过的那样,如果您的size方法被破坏,那么测试无论如何都会失败,因此您有理由在那里调查并了解为什么会发生这种情况。

无论如何,如果您仍然对测试之间的这种独立性感兴趣,您可以采用白盒测试策略:我猜您在BoundedPropertyBlockingQueue内部使用任何java.util集合、数组或来自其他提供者的集合实现(Guava、Apache您依赖的集合等),因此您无需验证这些结构是否按预期工作。

因此,将内部结构定义为protected,将您的测试类放在同名的包中,而不是依赖于size方法的实现,而是进入BoundedPropertyBlockingQueue

BoundedPriorityBlockingQueue q = BoundedPriorityBlockingQueue();
q.add(1);
assertEquals(1, q.contents.size());  // assuming that `contents` attribute is a collection.

主要缺点是现在如果您的队列的内部实现发生更改,您将需要更改测试,而使用以前的测试方法则不需要。

IMO 我会选择您当前的实现,耦合度较低,最终实现了其目标。

于 2013-09-26T12:50:26.313 回答
1

进行这样的交叉测试并没有错——一些方法往往成对存在(添加/删除、入队/出队等),如果没有补充部分,测试一个方法是没有意义的。

但是,我会更多地考虑add您的客户(班级用户)将如何使用该方法。很可能不会调用 add 来确定大小是否更改,而是稍后检索添加的项目。也许您的测试应该看起来更像这样:

BoundedPriorityBlockingQueue q = new BoundedPriorityBlockingQueue();
QueueItem toAdd = 1;
QueueItem added = q.dequeue();
assertEquals(toAdded, added);

最重要的是,您还可以将保护断言添加到上面的测试中(以确保队列不会以已添加的某些项目开始)甚至更好 - 包括保证队列初始状态的单独测试(大小为 0,出队返回 null/投掷)。

于 2013-09-26T13:31:42.227 回答