【问题标题】:How to properly match varargs in Mockito如何正确匹配 Mockito 中的可变参数
【发布时间】:2020-06-30 20:13:42
【问题描述】:

我一直在尝试使用 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));

这可行,尽管我在存根方法时完全省略了可变参数参数。

有什么线索吗?

【问题讨论】:

  • 最后一个例子有效的事实是相当微不足道的,因为它匹配零可变参数传递的情况。

标签: java mocking variadic-functions mockito


【解决方案1】:

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

【讨论】:

  • anyVararg() 将 Object 作为其返回类型。要使其与任何 var arg 类型(例如 String ...、Integer ... 等)兼容,请进行显式转换。例如,如果您有doSomething(Integer number, String ... args),您可以使用when(mock).doSomething(eq(1), (String) anyVarargs()) 之类的东西来执行模拟/存根代码。这应该可以解决编译错误。
  • 信息 anyVararg 现已被弃用:“@deprecated as of 2.1.0 use any()”
  • Matchers 现在已弃用,以避免与 org.hamcrest.Matchers 类发生名称冲突,并且可能会在 mockito v3.0 中删除。请改用ArgumentMatchers
  • anyVararg()ArgumentMatchers 中也已被弃用。
【解决方案2】:

一个有点未记录的特性:如果你想开发一个匹配可变参数的自定义匹配器,你需要让它实现 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);

【讨论】:

    【解决方案3】:

    以 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 中。
    • 我向 Hamcrest 提出了一个问题,以添加类似的内容。见github.com/mockito/mockito/issues/356
    • 这是为 Mockito 1 设计的吗?尝试针对 2.10 进行编译时出现各种编译错误。
    • @Frans 当我写这个答案时,看起来 2 版本仍处于测试阶段,所以是的,它可能是为 Mockito v1.10.19 左右编写的。 (github.com/mockito/mockito/releases) 它可能是可更新的... :-D
    【解决方案4】:

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

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

    arg1, arg2, arg3 是可变参数。

    【讨论】:

      【解决方案5】:

      基于 topchef 的回答,

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

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

      【讨论】:

      • 信息 anyVararg 现已弃用:“@deprecated as of 2.1.0 use any()”
      【解决方案6】:

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

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

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

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

      我正在使用模拟版本1.10.19

      【讨论】:

        【解决方案7】:

        你也可以遍历参数:

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

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

        【讨论】:

          【解决方案8】:

          改编自@topchef 的答案,

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

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

          【讨论】:

            【解决方案9】:

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

            【讨论】:

              【解决方案10】:

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

              【讨论】:

                【解决方案11】:

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

                ArgumentMatchers.any(类类型)

                实现中的代码是vararg aware

                reportMatcher(new InstanceOf.VarArgAware(

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

                any(String.class)
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-02-02
                  • 1970-01-01
                  • 2011-11-21
                  • 1970-01-01
                  相关资源
                  最近更新 更多