考虑使用包含以下方法签名的Mockito
模拟接口:
public void doThis(Object o);
public void doThis(Object... o)
我需要验证,doThis(Object o)
(而不是其他方法)已经被调用过一次。
首先,我认为以下几行可以解决问题:
verify(mock, times(1)).doThis(anyObject());
但是,由于这似乎适用于Windows,因此它不适用于Linux,因为在此环境中,需要调用其他doThis
方法。
造成这种情况的原因是 anyObject()
参数似乎与两种方法签名相匹配,而且一种选择或多或少是不可预测的。
如何强制 Mockito总是选择doThis(Object o)
进行验证?
答案 0 :(得分:2)
这不是一个模拟问题。
在进一步调查期间,I realized that the actual method is chosen at compile-time (JLS §15.12.2)。所以基本上windows和linux之间的类文件不同,导致了不同的mockito行为。
不建议使用该界面(请参阅Effective Java, 2nd Edition, Item 42
)。
我改变它以匹配以下内容:
public void doThis(Object o);
public void doThis(Object firstObj, Object... others)
通过此更改,将始终选择预期的(第一个)方法。
还剩下一件事:
为什么Windows上的java编译器(eclipse编译器)产生的输出与Linux上的Oracle JDK javac不同?
这可能是ECJ 中的一个错误,因为我希望这里的java语言规范非常严格。
答案 1 :(得分:2)
我同意其他答案中的大多数,只有一部分尚未得到答复:为什么编译器存在差异?
仔细看看这不是ecj和javac之间的区别,而是在不同版本的JLS之间:
让我们看看生成的字节码:
7: invokestatic #8 // Method anyObject:()Ljava/lang/Object;
10: checkcast #9 // class "[Ljava/lang/Object;"
13: invokevirtual #10 // Method doThis:([Ljava/lang/Object;)V
(两个编译器也同意这些字节)
这就是说:Java 8中的推理变得更加强大,现在能够推断出anyObject()
到Object[]
的类型参数。这可以通过checkcast
指令看到,该指令转换回源代码:(Object[])anyObject()
。这意味着在没有varargs魔法的情况下也可以调用doThis(Object...)
,但是通过传递Object[]
类型的单个参数。
现在两种方法都适用于同一类别("适用于固定的arity调用")并且在适用的方法中搜索最具体的方法选择第二种方法。
相比之下,Java 7只允许调用第二种方法作为变量调用,但如果找到固定的arity匹配则甚至不会尝试。
以上还说明了如何使程序对JLS中的更改具有鲁棒性:
verify(mock, times(1)).doThis(Matchers.<Object>anyObject());
这将告诉所有版本的所有编译器选择第一种方法,因为现在它总是会将doThis()
的参数看作Object
- 如果你真的无法避免这种不健康超载,即:)