Mockito如何在thenReturn块中处理带有多个参数的重叠匹配器?



我有一个测试代码块,它试图在一般情况下在后续调用中返回两个值,但在特定情况下仅返回与该情况相关的值。代码看起来像这样:

when(mockObject.method(anyString())).thenReturn(string1, string2);
when(mockObject.method(eq("expectedInput1"))).thenReturn(string1);
when(mockObject.method(eq("expectedInput2"))).thenReturn(string2);

预期的行为是当调用mockObject.method("foo")mockObject.method("bar")时,应该分别返回string1string2,但测试实际看到的是string2的两个响应。这是一个bug在Mockito ?还是我误解了Mockito模式匹配。

我的假设是最后匹配的模式是返回的,但是当经历这个过程时,Mockito是否分别处理第一个thenReturn块中的每个参数?有什么办法可以避免这种行为吗?

当我注释掉第二个调用时,模拟的行为与预期的一样,所以我假设重叠匹配器行为有一些特定的东西。

编辑:这是在Mockito版本1.9.5

我今天遇到了这个问题。这是由于调用模拟来设置存根,实际上消耗了已经存在的存根。

在本例中,将第一行更改为

when(mock.call(anyString())).thenReturn("","",string1,string2)

当您设置其他模拟返回时,这将给您两个空白响应,留下string1作为第一个有用的返回值。

也尝试doReturn替代,我认为可能没有这些问题:

doReturn(string1,string2).when(mock).call(anyString());

在安装过程中使用不同的存根。

所以我对此做了更多的研究。以下是我正在使用的函数,基于OP的问题:

    Function<String, String> function = mock(Function.class);
    when(function.apply(anyString())).thenReturn("A","B","C");
    when(function.apply("Jim")).thenReturn("Jim");
    when(function.apply("Bob")).thenReturn("Bob");
    assertThat(function.apply("Jim")).isEqualTo("Jim");
    assertThat(function.apply("Bob")).isEqualTo("Bob");
    assertThat(function.apply("")).isEqualTo("A");
    assertThat(function.apply("")).isEqualTo("B");
    assertThat(function.apply("")).isEqualTo("C");
    assertThat(function.apply("")).isEqualTo("C");

以上在isEqualTo("A")处失败,因为为JimBob设置模拟的两个调用使用了提供给anyString()的列表中的返回值。

您可能会试图重新排序when子句,但这失败了,因为anyString()取代了特殊情况,所以这也失败了。

上面的以下版本的DOES工作如预期:

    when(function.apply(anyString())).thenReturn("A","B","C");
    doReturn("Jim")
        .when(function)
        .apply("Jim");
    doReturn("Bob")
        .when(function)
        .apply("Bob");
    assertThat(function.apply("Jim")).isEqualTo("Jim");
    assertThat(function.apply("Bob")).isEqualTo("Bob");
    assertThat(function.apply("")).isEqualTo("A");
    assertThat(function.apply("")).isEqualTo("B");
    assertThat(function.apply("")).isEqualTo("C");
    assertThat(function.apply("")).isEqualTo("C");

这是因为doReturn技术旨在修改运行中的预先存在的模拟,实际上并不涉及调用模拟上的方法来设置模拟。

你可以使用doReturn的所有设置,而不是混合whenthenReturndoReturn ..when . .function()。实际上,这有点难看:

    doReturn("A").doReturn("B").doReturn("C")
        .when(function)
        .apply(anyString());

没有方便的varargs函数让您按顺序指定多个返回。上面的代码已经测试过了,可以正常工作。

很难说这是一个bug还是一个特性…问题是,当您调用mockObject.method(eq("expectedInput1"))执行第二次存根时,第一次存根已经到位。因此这个调用返回string1,然后将其无用地传递给when。随后的调用返回string2,这包括最后一次存根的调用和实际测试期间的后续调用。

我几乎看不到任何优雅的方法,除了使用自定义的Answer,就像@Nicolas建议的那样,尽管它看起来有点小题大做。也许,您可以使用自定义匹配器而不是anyString(),这实际上会说"任何字符串除了那两个"。这样你就不会有一个匹配器与另一个匹配器相交。

p。现在@Nicolas编辑了他的答案,这个正则表达式看起来就是我的意思。除了你根本不需要实现自定义匹配器。

解决这个问题的一种方法是使用正则表达式来避免重叠为下一个:

when(mockObject.method(eq("expectedInput1"))).thenReturn(string1);
when(mockObject.method(eq("expectedInput2"))).thenReturn(string2);
// Match with any input string that doesn't contain expectedInput1 neither expectedInput2
when(mockObject.method(matches("((?!expectedInput1|expectedInput2).)*")))
    .thenReturn(string1, string2);

例子:

System.out.println("expectedInput1=" + mockObject.method("expectedInput1"));
System.out.println("expectedInput2=" + mockObject.method("expectedInput2"));
System.out.println("foo=" + mockObject.method("foo"));
System.out.println("bar=" + mockObject.method("bar"));
System.out.println("bar=" + mockObject.method("bar"));
输出:

expectedInput1=string1
expectedInput2=string2
foo=string1
bar=string2
bar=string2

另一种方法可能是实现您的ArgumentMatcher仍然避免重叠:

when(mockObject.method(eq("expectedInput1"))).thenReturn(string1);
when(mockObject.method(eq("expectedInput2"))).thenReturn(string2);
when(
    mockObject.method(
        argThat(
            new ArgumentMatcher<String>(){
                @Override
                public boolean matches(final Object argument) {
                    return !"expectedInput1".equals(argument) 
                        && !"expectedInput2".equals(argument);
                }
            }
        )
    )
).thenReturn(string1, string2);

另一种方法是实现你的Answer这样做:

when(mockObject.method(anyString())).thenAnswer(
    new Answer<String>() {
        Iterator<String> it = Arrays.asList(string1, string2).iterator();
        String result;
        @Override
        public String answer(final InvocationOnMock invocation) throws Throwable {
            String argument = (String) invocation.getArguments()[0];
            switch (argument) {
                case "expectedInput1" :
                    return string1;
                case "expectedInput2" :
                    return string2;
                default:
                    if (it.hasNext()) {
                        result = it.next();
                    }
                    return result;
            }
        }
    }
);

相关内容

  • 没有找到相关文章

最新更新