考虑以下(完全人为的)示例:
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);
这似乎增加了很多复杂性,但没有太多价值(假设没有其他理由注入依赖项)。
在这个阶段,我倾向于使用第二种方法来公开常量,而不是在测试中对值进行硬编码。但这对我来说似乎并不理想。有更好的选择吗?或者,这些案例缺乏可测试性是否说明了设计缺陷?
正如Tim在评论中提到的,您的目标是确保您的软件按照规范运行。一个这样的规范可能是最大长度总是10,在这一点上,没有必要测试长度为5或15的世界。
这是一个要问自己的问题:您希望使用具有不同"常量"值的类的可能性有多大我在这里引用了"常量",因为如果你通过编程改变值,它根本不是一个常量,是吗?:)
-
如果你的值永远不会改变,你就根本不能使用符号常量,只需直接与10进行比较,并基于(比如)0、3、10和11进行测试。这可能会让你的代码和测试有点难以理解("10来自哪里?11来自哪里?"),如果你有理由改变数字,肯定会让你很难改变。不推荐。
-
如果您的值可能永远不会改变,您可以使用一个私有的命名常量(即静态的最终字段),正如您所做的那样。然后,您的代码将很容易更改,尽管您的测试将无法自动调整代码的方式。
-
您也可以放松,打包私有可见性,在同一个包中进行测试。Javadoc(例如
/** Package-private for testing. */
)或文档注释(例如@VisibleForTesting
)可能有助于明确您的意图。如果您的常量值是不透明的,并且在类之外不可用,例如URL模板或身份验证令牌,那么这是一个不错的选项。 -
您甚至可以将其设置为公共常量,这样您的类的消费者也可以使用它对于您的常量长度示例,公共静态最终字段可能是最好的,假设您的系统的其他部分可能想知道这一点(例如,UI验证提示或错误消息)。
-
-
如果您的值可能发生更改,您可以按实例接受它,如
new Length(10)
或new Length().setMaxLength(10)
。(我认为前者是一种依赖注入形式,将常数整数作为依赖项计数。)如果你想在测试中使用不同的值,比如在生产中使用2048的最大长度,但为了实用性起见,使用10进行测试,这也是一个好主意要制作一个灵活的长度验证器,这个选项可能是从静态final字段升级而来的一个好方法 -
只有当您的值在实例的生命周期内可能发生更改时,我才会使用DI样式的值提供程序。在这一点上,您可以交互式地查询该值,这样它的行为就不会像常量一样。对于"长度",这显然有些过头了,但对于"允许的最大内存"、"同时连接的最大数量"或其他类似的伪常数,这可能不是。
简而言之,你必须决定你需要多少控制,然后你可以从中选择最直接的选择;作为"默认值",您可能希望将其设置为可见字段或构造函数参数,因为它们往往在简单性和灵活性之间取得良好的平衡。