【发布时间】:2015-11-23 04:49:32
【问题描述】:
考虑以下(完全人为的)示例:
public class Length {
private static final int MAX_LENGTH = 10;
private final int length;
public Length(int length) {
if (length > MAX_LENGTH)
throw new IllegalArgumentException("Length too long");
this.length = length;
}
}
我想测试一下,当以大于MAX_LENGTH 的长度调用它时会引发异常。有多种测试方法,都有缺点:
@Test(expected = IllegalArgumentException.class)
public void testMaxLength() {
new Length(11);
}
这复制了测试用例中的常量。如果MAX_LENGTH 变小,这将不再是边缘情况(尽管显然它应该与单独的情况配对以测试边缘的另一侧)。如果它变得更大,这将失败并需要手动更改(这可能不是一件坏事)。
这些缺点可以通过为MAX_LENGTH 添加一个getter 来避免,然后将测试更改为:
new Length(Length.getMaxLength());
这似乎好多了,因为如果常量发生变化,则不需要更改测试。另一方面,它暴露了一个本来是私有的常量,并且它具有同时测试两种方法的重大缺陷 - 如果两种方法都被破坏,测试可能会给出误报。
另一种方法是根本不使用常量,而是注入依赖项:
interface MaxLength {
int getMaxLength();
}
public class Length {
public static void setMaxLength(MaxLength maxLength);
}
然后可以将“常量”作为测试的一部分进行模拟(此处使用 Mockito 的示例):
MaxLength mockedLength = mock(MaxLength.class);
when(mokedLength.getMaxLength()).thenReturn(17);
Length.setMaxLength(mockedLength);
new Length(18);
这似乎增加了很多复杂性而不是很多价值(假设没有其他理由注入依赖项)。
在这个阶段,我的偏好是使用第二种公开常量的方法,而不是硬编码测试中的值。但这对我来说似乎并不理想。有更好的选择吗?还是这些案例缺乏可测试性表明存在设计缺陷?
【问题讨论】:
-
单元测试的目的是在您的软件不符合您定义的规范时发出警报。如果您的
MAX_LENGTH常量由于某种原因突然减少,我认为您的测试用例中断是一件好事。换句话说,测试用例的目的不仅仅是让某些东西通过,而是检测代码中的异常情况。如果您不同意我的想法,请加入。 -
如果 MAX_LENGTH 是
public或protected,则单元测试可以使用 MAX_LENGTH+1。允许更多地访问您的代码以简化单元测试是一个备受争议的主题。 IMO 对常量执行此操作是可以的,但是 YMMV。 -
@user949300 我建议使用 package-private,而不是
public或protected,这样只有同一个包中的代码才能看到它,并且测试类应该在同一个包(虽然不同的源文件夹)。但是我更同意 Tim 的评论,即保留private,如果实现随着测试代码的相应更改而发生更改,则让测试失败,这是应该的。 -
@Andreas 很好的建议,如果在我的手机上输入“package-protected”不是那么烦人的话,我会添加的。 Java 的某些错误之一是将其用作默认可见性。
标签: java unit-testing junit mockito