繁体   English   中英

单元测试依赖于常量值的代码

[英]Unit testing code that relies on constant values

考虑以下(完全做作的)示例:

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模板或身份验证令牌),则这是一个不错的选项。

    • 你甚至可以把它变成一个公共常量,也可以为你的班级消费者提供。 对于常量Length的示例,公共静态final字段可能是最好的,假设您的系统的其他部分可能想知道(例如,UI验证提示或错误消息)。

  • 如果你的值可能会改变,你可以接受每个实例,如new Length(10)new Length().setMaxLength(10) (我认为前者是依赖注入的一种形式,将常量整数计为依赖。)如果你想在测试中使用不同的值,这也是一个好主意,例如在生产中使用最大长度2048但是为实用性而对10进行测试。 要制作灵活的长度验证器,此选项可能是静态最终字段的良好升级。

  • 只有在您的实例生命周期中您的价值可能会发生变化时 ,我才会为DI风格的价值提供者而烦恼。 此时,您可以以交互方式查询值,因此它根本不像常量那样表现。 对于“长度”,这显然是过度杀伤,但可能不是“最大允许内存”,“最大同时连接”或其他类似的伪常量。

简而言之,你必须决定你需要多少控制,然后你可以从那里选择最直接的选择; 作为“默认”,您可能希望将其设置为可见字段或构造函数参数,因为这些参数往往具有简单性和灵活性的良好平衡。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM