如何在 Mockito 正确匹配 varargs

我一直试图用 Mockito 模拟一个具有 vararg 参数的方法:

interface A {
B b(int x, int y, C... c);
}


A a = mock(A.class);
B b = mock(B.class);


when(a.b(anyInt(), anyInt(), any(C[].class))).thenReturn(b);
assertEquals(b, a.b(1, 2));

这样不行,但是如果我这样做:

when(a.b(anyInt(), anyInt())).thenReturn(b);
assertEquals(b, a.b(1, 2));

这是可行的,尽管我在存根化该方法时完全忽略了 varargs 参数。

有线索吗?

82331 次浏览

Mockito 1.8.1介绍了 任何 Vararg ()匹配器:

when(a.b(anyInt(), anyInt(), Matchers.<String>anyVararg())).thenReturn(b);

也可以查看历史: https://code.google.com/archive/p/mockito/issues/62

反对后编辑 新语法:

when(a.b(anyInt(), anyInt(), ArgumentMatchers.<String>any())).thenReturn(b);

一个没有文档说明的特性: 如果您想要开发一个匹配 vararg 参数的自定义 Matcher,您需要让它实现 org.mockito.internal.matchers.VarargMatcher以使其正确工作。它是一个空的标记接口,如果没有这个接口,Mockito 在使用 Matcher 调用带有 varargs 的方法时将无法正确地比较参数。

例如:

class MyVarargMatcher extends ArgumentMatcher<C[]> implements VarargMatcher {
@Override public boolean matches(Object varargArgument) {
return /* does it match? */ true;
}
}


when(a.b(anyInt(), anyInt(), argThat(new MyVarargMatcher()))).thenReturn(b);

以 Eli Levine 的回答为基础,我们提出了一个更为通用的解决方案:

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.mockito.ArgumentMatcher;
import org.mockito.internal.matchers.VarargMatcher;


import static org.mockito.Matchers.argThat;


public class VarArgMatcher<T> extends ArgumentMatcher<T[]> implements VarargMatcher {


public static <T> T[] varArgThat(Matcher<T[]> hamcrestMatcher) {
argThat(new VarArgMatcher(hamcrestMatcher));
return null;
}


private final Matcher<T[]> hamcrestMatcher;


private VarArgMatcher(Matcher<T[]> hamcrestMatcher) {
this.hamcrestMatcher = hamcrestMatcher;
}


@Override
public boolean matches(Object o) {
return hamcrestMatcher.matches(o);
}


@Override
public void describeTo(Description description) {
description.appendText("has varargs: ").appendDescriptionOf(hamcrestMatcher);
}


}

然后你可以把它用在 Hamcrest 的数组匹配器上,如下所示:

verify(a).b(VarArgMatcher.varArgThat(
org.hamcrest.collection.IsArrayContaining.hasItemInArray("Test")));

(显然静态导入会使这个更具可读性。)

基于顶级厨师的回答,

对于2.0.31-beta 版,我必须使用 Mockito.anyVararg 而不是 Matchers.anyVararrg:

when(a.b(anyInt(), anyInt(), Mockito.<String>anyVararg())).thenReturn(b);

在我的例子中,我想要捕获其参数的方法的签名是:

    public byte[] write(byte ... data) throws IOException;

在这种情况下,您应该明确地转换为 字节数组:

       when(spi.write((byte[])anyVararg())).thenReturn(someValue);

我用的是仿真版 1.10.19

我一直在使用 Peter Westmacott 的回答中的代码,然而在 Mockito 2.2.15中,你现在可以做以下事情:

verify(a).method(100L, arg1, arg2, arg3)

其中 arg1, arg2, arg3是 varargs。

你也可以循环使用这些参数:

Object[] args = invocation.getArguments();
for( int argNo = 0; argNo < args.length; ++argNo) {
// ... do something with args[argNo]
}

例如,检查它们的类型并适当地强制转换它们,添加到列表或其他任何东西。

根据“顶级厨师”的回答,

Mockito.when(a.b(Mockito.anyInt(), Mockito.anyInt(), Mockito.any())).thenReturn(b);

根据 Mockito 2.23.4的 java 文档,Mockito.any ()“匹配任何东西,包括 null 和 varargs。”

您可以通过传递一个 ArgumentCaptor 捕获,然后使用“ getAllValue”以列表的形式检索 varargs 来实现这一点,请参见: https://stackoverflow.com/a/55621731/11342928

由于其他答案都有意义,并且使测试能够明显地工作,我仍然建议测试,就好像该方法没有使用 vararg,而是使用常规的定义良好的参数。这有助于在与可能的模糊参数相关的重写方法到位的情况下,比如 SLF4J 日志记录器:

测试:

jobLogger.info("{} finished: {} tasks processed with {} failures, took {}", jobName, count, errors, duration);

这里有很多重写,重要的方法是这样声明的

Logger.info(String, Object...)

核实:

verify(loggerMock).info(anyString(), anyString(), anyInt(), anyInt(), anyString());

证明上面的工作原理是 errors是一个整数而不是一个长数,因此下面的代码不会运行:

verify(loggerMock).info(anyString(), anyString(), anyInt(), anyLong(), anyString());

因此,您可以很容易地使用 when()代替 verify()-stuff 来设置所需的返回值。

而且它可能显示了更多的意图,更具可读性。捕获也可以在这里使用,这样更容易访问。

使用 Mockito 2.15进行测试

我必须使用 any (Class type)方法来匹配作为 varargs 参数传递的数组参数。

Any (类类型)

实现中的代码是 Vararg 感知

ReportMatcher (新的 InstanceOf. VarArgAware (

在我的例子中,将 String [] arg 匹配到 String... param 的方法是:-

any(String.class)