简体   繁体   English

Mockito:使用类型兼容的参数验证重载方法

[英]Mockito: Verifying overloaded methods with type-compatible arguments

Consider you want to mock an interface using Mockito containing the following method signatures: 考虑您想使用包含以下方法签名的Mockito模拟界面:

public void doThis(Object o);

public void doThis(Object... o)

I need to verify that doThis(Object o) (and not the other method) has been invoked exactly one time. 我需要验证 doThis(Object o) (而不是其他方法)已经被调用了一次。

First I thought that the following line would do the trick: 首先,我认为以下行可以解决问题:

verify(mock, times(1)).doThis(anyObject());

However, as this seems to work on Windows, it doesn't on Linux because in this environment, a call to of the other doThis method is expected. 但是,由于这似乎适用于Windows,因此在Linux上不会,因为在此环境中,需要调用另一个doThis方法。
This is caused because the anyObject() argument seems to match both method signatures and one is chosen in a more or less unpredictable way. 造成这种情况的原因是anyObject()参数似乎与两个方法签名都匹配,而且一个或多或少是以不可预测的方式选择。

How can I enforce that Mockito always chooses doThis(Object o) for verification? 我如何强制Mockito总是选择doThis(Object o)进行验证?

This is not a mockito issue. 这不是一个模拟问题。

During further investigating, I realized that the actual method is chosen at compile-time (JLS §15.12.2) . 在进一步调查期间, 我意识到实际方法是在编译时选择的(JLS§15.12.2) So basically the class files differed between windows and linux which caused different mockito behavior. 所以基本上windows和linux之间的类文件不同导致了不同的mockito行为。

The interface is discouraged (see Effective Java, 2nd Edition, Item 42 ). 不鼓励使用该接口(参见Effective Java, 2nd Edition, Item 42 )。 I changed it to match the following: 我改变它以匹配以下内容:

public void doThis(Object o);

public void doThis(Object firstObj, Object... others)

With this change the expected (first) method will always be chosen. 通过此更改,将始终选择预期(第一)方法。

One thing is still left: 还剩下一件事:
Why does the java compiler on windows (eclipse compiler) produce a different output than Oracle JDK javac on Linux? 为什么Windows上的java编译器(eclipse编译器)产生的输出与Linux上的Oracle JDK javac不同?

This might be a bug in ECJ because I would expect that the java language spec is pretty strict here. 这可能是ECJ中的一个错误,因为我希望java语言规范在这里非常严格。

I agree to most from the other answer, just one part hasn't been answered yet: why the difference in compilers? 我同意大多数其他答案,只有一部分尚未得到答复:为什么编译器的差异?

At a closer look this is not a difference between ecj and javac, but between different versions of JLS: 仔细看看这不是ecj和javac之间的区别,而是在不同版本的JLS之间:

  • compiling at compliance 1.7 or less, both compilers select the first (single argument) method. 在符合1.7或更低的情况下编译,两个编译器都选择第一个(单个参数)方法。
  • compiling at compliance 1.8, both compilers select the second (varargs) method 在符合1.8的情况下编译,两个编译器都选择第二种(varargs)方法

Let's have a look at the produced bytecodes: 我们来看看生成的字节码:

     7: invokestatic  #8                  // Method anyObject:()Ljava/lang/Object;
    10: checkcast     #9                  // class "[Ljava/lang/Object;"
    13: invokevirtual #10                 // Method doThis:([Ljava/lang/Object;)V

(both compilers also agree on these bytes) (两个编译器也同意这些字节)

This is to say: inference in Java 8 has become more powerful and is now able to infer the type argument for anyObject() to Object[] . 这就是说:Java 8中的推理变得更加强大,现在能够推断anyObject()Object[]的类型参数。 This can be seen by the checkcast instruction, which translated back into source code reads: (Object[])anyObject() . 这可以通过checkcast指令看到,它转换回源代码读取: (Object[])anyObject() This implies that also doThis(Object...) can be invoked without varargs magic, but by passing a single argument of type Object[] . 这意味着doThis(Object...)也可以在没有varargs魔法的情况下调用,但是通过传递Object[]类型的单个参数。

Now both methods are applicable in the same category ("applicable by fixed arity invocation") and search for the most specific among applicable methods selects the second method. 现在两种方法都适用于同一类别(“通过固定的arity调用适用”),并且在适用的方法中搜索最具体的方法选择第二种方法。

By contrast, Java 7 would allow invoking the second method only as a variable-arity invocation, but this is not even tried if a fixed-arity match is found. 相比之下,Java 7只允许调用第二种方法作为变量调用,但如果找到固定的arity匹配,甚至不会尝试这种方法。

The above also indicates how to make the program robust against the changes in JLS: 以上还说明了如何使程序对JLS中的更改具有可靠性:

verify(mock, times(1)).doThis(Matchers.<Object>anyObject());

This will tell all compilers at all versions to select the first method, because now it will always see the argument of doThis() as Object -- if you really can't avoid this unhealthy overload, that is :) 这将告诉所有版本的所有编译器选择第一个方法,因为现在它总是会看到doThis()的参数为Object - 如果你真的无法避免这种不健康的重载,那就是:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM