如何正确匹配Mockito中的varargs

时间:2010-04-13 17:08:50

标签: java mocking variadic-functions mockito

我一直试图使用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参数,但这仍然有效。

任何线索?

10 个答案:

答案 0 :(得分:207)

Mockito 1.8.1介绍了anyVararg() matcher

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);

答案 1 :(得分:29)

一个有点无法记录的功能:如果你想开发一个匹配vararg参数的自定义匹配器,你需要让它实现org.mockito.internal.matchers.VarargMatcher才能正常工作。这是一个空的标记界面,没有它,Mockito在使用你的匹配器调用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);

答案 2 :(得分:5)

在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")));

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

答案 3 :(得分:3)

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

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

其中arg1, arg2, arg3是varargs。

答案 4 :(得分:1)

以topchef的答案为基础,

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

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

答案 5 :(得分:0)

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

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

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

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

我正在使用mockito版本1.10.19

答案 6 :(得分:0)

你也可以遍历参数:

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

例如检查他们的类型并适当地投射它们,添加到列表或其他任何内容。

答案 7 :(得分:0)

适应@topchef的答案,

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

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

答案 8 :(得分: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 测试

答案 9 :(得分:-1)

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