Java Mockito:验证具有类型兼容参数的重载方法
假设您希望使用包含以下方法签名的Java Mockito:验证具有类型兼容参数的重载方法,java,eclipse,mockito,overload-resolution,ecj,Java,Eclipse,Mockito,Overload Resolution,Ecj,假设您希望使用包含以下方法签名的Mockito模拟接口: public void doThis(Object o); public void doThis(Object... o) 我需要验证这(对象o)(而不是另一个方法)是否只调用了一次 首先,我认为下面这句话可以达到目的: verify(mock, times(1)).doThis(anyObject()); 但是,由于这在Windows上似乎有效,因此在Linux上不起作用,因为在此环境中,需要调用另一个方法来调用此方法。 这是因为
Mockito
模拟接口:
public void doThis(Object o);
public void doThis(Object... o)
我需要验证这(对象o)
(而不是另一个方法)是否只调用了一次
首先,我认为下面这句话可以达到目的:
verify(mock, times(1)).doThis(anyObject());
但是,由于这在Windows上似乎有效,因此在Linux上不起作用,因为在此环境中,需要调用另一个方法来调用此方法。
这是因为anyObject()
参数似乎与两个方法签名都匹配,并且以或多或少不可预测的方式选择了一个
我如何强制执行Mockito始终选择doThis(Object o)
进行验证?这不是Mockito的问题。
在进一步调查中。因此,windows和linux之间的类文件基本上是不同的,这导致了不同的mockito行为
不鼓励使用该接口(请参阅《有效Java》,第二版,第42项)。
我将其更改为与以下内容相匹配:
public void doThis(Object o);
public void doThis(Object firstObj, Object... others)
通过此更改,将始终选择预期(第一)方法
还剩下一件事:
为什么windows上的java编译器(eclipse编译器)产生的输出与Linux上的Oracle JDK javac不同
这可能是ECJ中的一个错误,因为我认为java语言规范在这里是非常严格的。我同意另一个答案中的大部分,只是有一部分还没有得到回答:为什么编译器之间存在差异
仔细看,这不是ecj和javac之间的区别,而是JLS的不同版本之间的区别:
- 在compliance 1.7或更低版本下编译时,两个编译器都选择第一个(单参数)方法
- 在compliance 1.8下编译时,两个编译器都选择第二个(varargs)方法
让我们看看产生的字节码:
7: invokestatic #8 // Method anyObject:()Ljava/lang/Object;
10: checkcast #9 // class "[Ljava/lang/Object;"
13: invokevirtual #10 // Method doThis:([Ljava/lang/Object;)V
(两个编译器也同意这些字节)
这就是说:Java8中的推理功能变得更加强大,现在能够推断anyObject()
到Object[]
的类型参数。这可以通过checkcast
指令看到,该指令被翻译回源代码,读取:(Object[])anyObject()
。这意味着也可以在不使用varargs magic的情况下调用doThis(Object…
,但只需传递一个Object[]
类型的参数即可
现在这两种方法都适用于同一类别(“通过固定算术调用适用”),搜索适用方法中最具体的方法将选择第二种方法
相比之下,Java7只允许以变量arity调用的形式调用第二个方法,但如果找到了固定的arity匹配项,就不会尝试这样做
上述内容还说明了如何使计划对JLS中的变化具有鲁棒性:
verify(mock, times(1)).doThis(Matchers.<Object>anyObject());
verify(mock,times(1)).doThis(Matchers.anyObject());
这将告诉所有版本的编译器都选择第一种方法,因为现在它总是会看到<代码> DOTHESE()/<代码>作为<代码>对象< /代码>——如果你真的无法避免这种不健康的超载,那就是:
听起来有点像你定义的JavaC无bug。