Java Mockito-是否有一个“匹配者”;值不在列表中;?

Java Mockito-是否有一个“匹配者”;值不在列表中;?,java,unit-testing,junit,mockito,Java,Unit Testing,Junit,Mockito,我目前有一个mock,它对特定的输入集有特定的行为。 其他每一个输入都应该返回一个特定的响应 例如: Mockito.when(classInstance.myFunc(Mockito.eq("Option1"))).thenReturn(answer1); Mockito.when(classInstance.myFunc(Mockito.eq("Option2"))).thenReturn(answer2); Mockito.when(classInstance.my

我目前有一个mock,它对特定的输入集有特定的行为。 其他每一个输入都应该返回一个特定的响应

例如:

    Mockito.when(classInstance.myFunc(Mockito.eq("Option1"))).thenReturn(answer1);
    Mockito.when(classInstance.myFunc(Mockito.eq("Option2"))).thenReturn(answer2);
    Mockito.when(classInstance.myFunc(Mockito.eq("Option3"))).thenReturn(answer3);
    Mockito.when(classInstance.myFunc(Mockito.eq("Option4"))).thenReturn(answer4);

    // Return defaultAnswer if and(and(not("Option1"), not("Option2")), and(not("Option3"), not("Option4")))
    Mockito.when(classInstance.myFunc(AdditionalMatchers.and(AdditionalMatchers.and(AdditionalMatchers.not(Mockito.eq("Option1")), AdditionalMatchers.not(Mockito.eq("Option2")), AdditionalMatchers.and(AdditionalMatchers.not(Mockito.eq("Option3")), AdditionalMatchers.not(Mockito.eq("Option4")))).thenReturn(defaultAnswer);
我最大的问题是
和(和(不是(“选项1”)、不是(“选项2”)、和(不是(“选项3”)、不是(“选项4”))
行的复杂性

我真的希望有一种更简单的方法来指定“其他一切”或“不在列表中:[“选项1”,…]”的条件

“团队内”或类似的东西有匹配者吗

我目前有一个mock,它对特定的输入集有特定的行为。其他每一个输入都应该返回一个特定的响应

就像,错了

单元测试的目的是让您快速找到bug。为了理解正在发生的事情,您希望单元测试尽可能地“自包含”。您阅读了测试,可能是安装方法,并且了解了正在发生的事情(或者至少“进入”正在测试的代码)

相反,拥有多个规格来覆盖多个案例并没有帮助。你不会想要的

相反,您需要尽可能少的规范。如果您的模拟看到不同的输入,并且应该返回不同的结果,那么这应该是每个测试用例都要做的事情。在您的设置方法中不是“一般”


因此,不同的非答案是:避免使用多个这样的规格。

为什么不简单地使用
Mockito.matches(布尔)
,例如:

 import static org.mockito.Mockito.*;

 Mockito.when(classInstance.myFunc(matches("Option[^1-4]"))
        .thenReturn(defaultAnswer);
您也可以使用
Mockito.argThat()

要过滤掉一些整数值(如您评论中所建议的),您可以写:

 import static org.mockito.Mockito.*;

 List<Integer> toNotMatchList = Arrays.asList(1, 2, 3, 4) ;
 Mockito.when(classInstance.myFunc(argThat(i -> !toNotMatchList.contains(i))
        .thenReturn(defaultAnswer);

通过显式定义默认值,然后使用后续存根“覆盖”该值,您可能会使其更具可读性:

when(classInstance.myFunc(any()).thenReturn(defaultAnswer);
when(classInstance.myFunc("Option1").thenReturn(answer1);
when(classInstance.myFunc("Option2").thenReturn(answer2);
...
或者您可以使用MockitoHamcrest和Hamcrest的核心匹配器来简化它,例如:

when(classInstance.myFunc(argThat(is(allOf(not("Option1"), not("Option2"), ...))))
    .thenReturn(...)

或者你可以使用MockitoHamcrest和你自己的Hamcrest匹配器。

如果你在另一个
eq()
mock之后使用
anyString()
会发生什么?很好!简单的解决方案总是比复杂的解决方案好。我仍然认为在使用这样的多个规范时应该小心,但至少您的代码是很好的人类可读性!谢谢那可能行得通。但实际上我需要它来表示整数,“Option1”只是一个example@GhostCat我完全同意你的看法。为模拟指定的夹具太多,无法直接读取和维护。@GuyKhmel不客气。在这种情况下,只需使用
ArgumentMatcher
。不需要harmcrest或任何第三个库,只需要最新的Mockito版本(2)。我更新了。这是非常酷的,现在有了Java8流和Mockito2。然而,使用Hamcrest匹配器的优点仍然是使用它们而不是直接的谓词可以获得更好的失败消息。但是,第一种方法确实有一个缺点。如果在
any()
matcher之前修改代码为同一模拟方法添加模拟录制,它将被any()matcher覆盖。我认为这是否是一个缺点仍有争议。我使用的一种常见模式是在
@之前的
@中定义这样的“
any()
”默认值存根,然后在测试本身中使用存根/模拟来覆盖这些存根/模拟,这些存根/模拟具有测试相关的返回值或验证其参数。这有助于维持高特异性、低噪声测试。如果之前的存根继续像您建议的那样对后续的存根产生影响,我认为这是行不通的。在
@Before
钩子方法中的默认模拟记录很好!我喜欢你的非答案。我最近写了两封信:)
when(classInstance.myFunc(argThat(is(allOf(not("Option1"), not("Option2"), ...))))
    .thenReturn(...)