如何在Mockito中正确匹配varargs


152

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

有什么线索吗?


最后一个示例有效的事实是微不足道的,因为它与传递零个varargs参数的情况相匹配。
topchef

Answers:


235

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

26
anyVararg()具有Object作为其返回类型。为了使其与任何var arg类型(例如String ...,Integer ...等)兼容,请进行显式转换。例如,如果有的doSomething(Integer number, String ... args)话,可以使用进行模拟/存根代码when(mock).doSomething(eq(1), (String) anyVarargs())。那应该注意编译错误。
Psycho Punch

15
有关信息,现在不建议使用anyVararg:“自2.1.0起不建议使用@(使用any()”)
alexbt

5
Matchers为了避免与org.hamcrest.Matchers类发生名称冲突,现在已弃用该符号,并且可能会在嘲笑v3.0中将其删除。使用ArgumentMatchers代替。
JonyD

31

一个未公开的功能:如果要开发与vararg参数匹配的自定义Matcher,则需要实现它 org.mockito.internal.matchers.VarargMatcher以使其正常工作。这是一个空的标记接口,如果没有Matcher调用带有varargs的方法,则Mockito将无法正确比较参数。

例如:

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

7

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

(显然,静态导入将使其更具可读性。)


真好 这应该内置在Mockito IMO中。
bryant

我针对Hamcrest提出了一个添加此类内容的问题。参见github.com/mockito/mockito/issues/356
马克

这是Mockito 1吗?尝试针对2.10进行编译时,遇到各种编译错误。
弗朗斯

@Frans在我编写此答案时,似乎2版本仍处于beta中,因此,是的,它可能是为Mockito v1.10.19或其附近版本编写的。(github.com/mockito/mockito/releases)可能是可更新的... :-D
Peter Westmacott,

3

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

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

arg1, arg2, arg3varargs 在哪里。


1

基于topchef的答案,

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

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

3
有关信息,现在不建议使用anyVararg:“自2.1.0起不建议使用@(使用any()”)
alexbt

0

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

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

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

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

我正在使用模仿版本 1.10.19


0

您还可以遍历参数:

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

例如,检查其类型并将其正确转换,添加到列表或其他内容。


0

改编@topchef的答案,

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

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


0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.