Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/401.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 依赖于常量值的单元测试代码_Java_Unit Testing_Junit_Mockito - Fatal编程技术网

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

Java 依赖于常量值的单元测试代码,java,unit-testing,junit,mockito,Java,Unit Testing,Junit,Mockito,考虑以下(完全人为的)示例: 我想测试一下,当使用大于MAX\u length的长度调用时,这是否会引发异常。有很多方法可以测试这一点,但都有缺点: @Test(expected = IllegalArgumentException.class) public void testMaxLength() { new Length(11); } 这将复制测试用例中的常量。如果MAX_LENGTH变小,这将不再是一个边缘案例(尽管它显然应该与一个单独的案例配对以测试边缘的另一侧)。如果它变得

考虑以下(完全人为的)示例:

我想测试一下,当使用大于
MAX\u 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的示例):

这似乎增加了很多复杂性,但没有太多的价值(假设没有其他理由注入依赖项)

在这个阶段,我倾向于使用第二种方法公开常量,而不是在测试中硬编码值。但这在我看来并不理想。有更好的选择吗?或者这些案例缺乏可测试性是否表明存在设计缺陷?

如评论中所述,您的目标是确保您的软件按照规范运行。一个这样的规范可能是最大长度始终为10,在这一点上,没有必要测试长度为5或15的世界

这里有一个问题要问你自己:你想用不同的“常量”值来使用你的类的可能性有多大?我在这里引用了“常量”,因为如果以编程方式更改值,它实际上根本不是常量,是吗?:)

  • 如果您的值永远不会改变,那么您根本不能使用符号常量,只需直接与10进行比较,并基于(比如)0、3、10和11进行测试。这可能会使您的代码和测试有点难以理解(“10是从哪里来的?11是从哪里来的?”),并且如果您确实有理由更改数字,那么肯定会很难更改。不推荐

  • 如果您的值可能永远不会更改,您可以使用私有命名常量(即静态最终字段),正如您所做的那样。然后,您的代码将很容易更改,尽管您的测试无法自动调整代码的方式

    • 您还可以放宽到包私有可见性,这将可用于同一包中的测试。Javadoc(例如,
      /**测试专用包。*/
      )或文档注释(例如)可能有助于明确您的意图。如果您的常量值是不透明的,并且在类之外不可用,例如URL模板或身份验证令牌,那么这是一个不错的选择

    • 您甚至可以将其设置为公共常量,您的类的消费者也可以使用它。对于恒定长度的示例,公共静态final字段可能是最好的,前提是您的系统的其他部分可能想知道这一点(例如,UI验证提示或错误消息)

  • 如果您的值可能会更改,您可以根据每个实例接受它,如
    newlength(10)
    newlength()。setMaxLength(10)
    。(我认为前者是依赖注入的一种形式,将常数整数计数为依赖项)。如果您想在测试中使用不同的值,这也是一个好主意,例如在生产中使用最大长度为2048,但对于实用性而言,测试为10。要制作一个灵活的长度验证器,这个选项可能是从静态final字段的一个很好的升级

  • 只有当您的值可能在实例的生命周期内发生变化时,我才会使用DI样式的值提供程序。此时,您可以交互地查询该值,因此它的行为根本不像常量。对于“长度”,这显然是过度使用,但对于“最大允许内存”、“最大同时连接数”或其他类似的伪常量,可能不是这样


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

单元测试的目的是在软件的行为不符合您定义的规范时标记警报。如果你的常数<代码>最大长度< /COD>由于任何原因突然减少,我认为你的测试用例会中断是一件好事。换句话说,测试用例的目的不仅仅是通过测试,而是检测代码中的异常。如果你不同意我的想法,请插话。如果MAX_LENGTH是
public
protected
,那么单元测试可以使用MAX_LENGTH+1。允许更多地访问代码以简化单元测试是一个有很多争议的话题。对于常量,我这样做是可以的,但是YMMV@user949300我建议使用包private,而不是
public
protected
,这样只有同一个包中的代码才能看到它,并且测试类应该在同一个包中(尽管源文件夹不同)。不过,我更同意Tim的意见,也就是说,如果实现随着测试代码的相应更改而更改,就让测试失败,这是应该的。@Andreas是一个很好的建议,我同意
interface MaxLength {
    int getMaxLength();
}

public class Length {
    public static void setMaxLength(MaxLength maxLength);
}
MaxLength mockedLength = mock(MaxLength.class);
when(mokedLength.getMaxLength()).thenReturn(17);
Length.setMaxLength(mockedLength);
new Length(18);