如何正确匹配Mockito中的varargs

qua*_*ial 142 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));
Run Code Online (Sandbox Code Playgroud)

这不起作用,但是如果我这样做:

when(a.b(anyInt(), anyInt())).thenReturn(b);
assertEquals(b, a.b(1, 2));
Run Code Online (Sandbox Code Playgroud)

尽管我在删除方法时完全省略了varargs参数,但这仍然有效.

有线索吗?

top*_*hef 217

Mockito 1.8.1推出了anyVararg()匹配器:

when(a.b(anyInt(), anyInt(), Matchers.<String>anyVararg())).thenReturn(b);
Run Code Online (Sandbox Code Playgroud)

另请参阅历史记录:https://code.google.com/archive/p/mockito/issues/62

弃用后编辑新语法:

when(a.b(anyInt(), anyInt(), ArgumentMatchers.<String>any())).thenReturn(b);
Run Code Online (Sandbox Code Playgroud)

  • `anyVararg()`有Object作为返回类型.要使其与任何var arg类型(例如String ...,Integer ...等)兼容,请执行显式转换.例如,如果你有`doSomething(Integer number,String ... args)`你可以用`when(mock).doSomething(eq(1),(String)anyVarargs())来做mock/stub代码. `.这应该照顾编译错误. (26认同)
  • for info anyVararg现已弃用:"@preprecated from 2.1.0 use any()" (15认同)
  • `anyVararg()` 也已在 `ArgumentMatchers` 中被弃用。 (5认同)
  • `Matchers`现在已被弃用,以避免与`org.hamcrest.Matchers`类发生名称冲突,并且可能会在mockito v3.0中删除.请改用"ArgumentMatchers". (4认同)
  • 注意:如果您遇到不明确的方法调用问题(例如,一个方法采用单个参数,而另一种方法采用可变参数),您可以通过将“any”方法强制转换为类型化数组来解决该问题。例如:“(String[])any()”。 (2认同)

小智 30

一个有点未记录的功能:如果你想开发一个匹配vararg参数的自定义匹配器,你需要让它实现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);
Run Code Online (Sandbox Code Playgroud)


Mar*_*ark 8

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

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

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


Pet*_*ott 6

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

}
Run Code Online (Sandbox Code Playgroud)

然后你可以将它与hamcrest的数组匹配器一起使用:

verify(a).b(VarArgMatcher.varArgThat(
            org.hamcrest.collection.IsArrayContaining.hasItemInArray("Test")));
Run Code Online (Sandbox Code Playgroud)

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