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

Mockito: Verifying overloaded methods with type-compatible arguments

考虑到您想要 mock 一个使用 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) 进行验证?

这不是 mockito 问题。

在进一步调查中,。所以基本上 class 文件在 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 语言规范在这里非常严格。

我同意其他答案中的大部分,只有一部分尚未回答:为什么编译器不同?

仔细一看,这不是ecj和javac之间的区别,而是不同版本的JLS之间的区别:

  • 编译符合 1.7 或更低,两个编译器 select 第一个(单参数)方法。
  • 编译符合 1.8,两个编译器 select 第二种(可变参数)方法

让我们看看生成的字节码:

     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()。这意味着 doThis(Object...) 也可以在没有可变参数魔法的情况下被调用,但是通过传递类型为 Object[].

的单个参数

现在这两种方法都适用于同一类别("applicable by fixed arity invocation")并在适用的方法中搜索最具体的select第二种方法。

相比之下,Java 7 将允许仅将第二种方法作为可变元数调用来调用,但如果找到固定元数匹配,则甚至不会尝试此方法。

以上也说明了如何使程序对JLS的变化具有健壮性:

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

这将告诉所有版本的所有编译器 select 第一种方法,因为现在它总是将 doThis() 的参数视为 Object -- 如果你真的可以'不要避免这种不健康的超载,即 :)