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



考虑您想要使用包含以下方法签名的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问题

在进一步的调查中,我意识到实际的方法是在编译时选择的(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语言规范非常严格。

我同意另一个答案中的大部分,只是有一部分还没有得到回答:为什么编译器不同?

仔细一看,这不是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

(两个编译器也同意这些字节)

也就是说:Java 8中的推理变得更加强大,现在可以推断anyObject()Object[]的类型参数。这可以从checkcast指令中看出,该指令被翻译回源代码:(Object[])anyObject()。这意味着doThis(Object...)也可以在没有varargs魔术的情况下调用,而是通过传递类型为Object[]的单个参数来调用。

现在,这两种方法都适用于同一类别("通过固定arity调用适用"),并且在适用方法中搜索最具体的方法会选择第二种方法。

相比之下,Java7只允许将第二个方法作为变量arity调用来调用,但如果找到固定的arity匹配,则不会尝试这样做。

上面还说明了如何使程序在JLS:中的变化中保持稳健

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

这将告诉所有版本的编译器选择第一个方法,因为现在它总是将doThis()的参数视为Object——如果你真的无法避免这种不健康的重载,那就是:)

相关内容

  • 没有找到相关文章

最新更新