173

我一直在尝试使用 Mockito 来模拟带有可变参数的方法:

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 参数,但这仍然有效。

有什么线索吗?

4

11 回答 11

261

Mockito 1.8.1 引入了anyVararg() 匹配器

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);
于 2010-04-14T02:53:21.963 回答
32

一个有点未记录的特性:如果你想开发一个匹配可变参数的自定义匹配器,你需要实现org.mockito.internal.matchers.VarargMatcher它才能正常工作。这是一个空的标记接口,没有它,Mockito 在使用 Matcher 调用带有可变参数的方法时将无法正确比较参数。

例如:

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);
于 2011-11-03T00:15:02.410 回答
9

基于 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")));

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

于 2015-06-30T15:40:21.053 回答
3

我一直在使用 Peter Westmacott 的答案中的代码,但是使用 Mockito 2.2.15 您现在可以执行以下操作:

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

arg1, arg2, arg3可变参数在哪里。

于 2016-11-14T09:26:08.733 回答
2

基于topchef的回答,

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

when(a.b(anyInt(), anyInt(), Mockito.<String>anyVararg())).thenReturn(b);
于 2016-04-29T17:44:37.643 回答
0

您还可以遍历参数:

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

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

于 2017-02-10T12:20:44.623 回答
0

在我的情况下,我想要捕获其参数的方法的签名是:

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

在这种情况下,您应该显式转换为字节数组

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

我正在使用模拟版本1.10.19

于 2016-05-03T18:31:32.883 回答
0

由于其他答案很有意义并使测试工作明显,我仍然建议测试方法好像该方法没有采用可变参数,而是采用常规的明确定义的参数。这有助于在与可能的模糊参数相关的重写方法存在的情况下,例如 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 测试

于 2021-06-14T07:22:43.390 回答
0

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

ArgumentMatchers.any(类类型)

实现中的代码是可变参数感知的。

reportMatcher(新 InstanceOf.VarArgAware(

在我将 String[] arg 与 String... 参数匹配的情况下,以下工作:-

any(String.class)
于 2021-08-24T12:19:30.517 回答
0

改编来自@topchef的答案,

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

根据 Mockito 2.23.4 的 java 文档,Mockito.any() “匹配任何东西,包括空值和可变参数。”

于 2019-01-25T14:42:19.200 回答
0

您可以通过传递 ArgumentCaptor 捕获然后使用“getAllValues”将可变参数作为列表检索来完成此操作,请参阅:https ://stackoverflow.com/a/55621731/11342928

于 2019-04-10T21:47:34.647 回答