我有一个类Sut
,其中使用java.util.function.Supplier
实现了延迟初始化。实际上,下面的代码更加复杂,但这是Mockito无法测试的最简单形式。以下测试引发错误Wanted but not invoked ... However, there were other interactions with this mock
。 Mockito为什么不计算create
的调用?代码流实际上输入create()
;我用调试器检查过。
import java.util.function.Supplier;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
public class TestTimes {
@Test
public void testCreateOnlyOnce() {
Sut sut = spy(new Sut());
sut.getData();
sut.getData();
sut.getData();
verify(sut, times(1)).create();
}
private static class Sut {
Supplier<Object> data = this::create;
void getData() {
data.get();
}
Object create() {
return new Object();
}
}
}
答案 0 :(得分:3)
首先,感谢您的书面问题。
我已经亲自测试了您的代码,并看到了您提到的错误。虽然,我在调试时对您的代码进行了一些更改...看一下:
@Test
public void testCreateOnlyOnce() {
Sut sut = spy(new Sut());
sut.getData();
sut.getData();
sut.getData();
verify(sut, times(1)).create();
}
private static class Sut {
private Supplier<Object> data;
// Added de data object initialization on the constructor to help debugging.
public Sut() {
this.data = this::create;
}
void getData() {
data.get();
}
Object create() {
return new Object();
}
}
我在调试时发现的东西
Sut
子句中正确调用了spy(new Sut())
类构造函数,但是在此处没有调用create()
方法。sut.getData()
时,也会调用create()
方法。是什么使我得出结论,最终:在构造函数上,
this::create
所做的全部是告诉Java,只要需要从供应商那里获取Object
,就会从{{1}中获取Object
。 } 方法。而且,供应商调用的create()
方法来自与Mockito所监视的类实例不同的类实例。
这说明了为什么无法通过验证来跟踪它。
编辑:根据我的研究,这实际上是供应商的期望行为。它只是创建一个具有create()
方法的接口,该接口调用您在方法参考中声明的noArgs方法。看看“使用方法参考来实例化供应商”上的this。
答案 1 :(得分:3)
我想给加布里埃尔·皮门塔斯(Gabriel Pimentas)好的答案再加一点点。之所以如此行之有效的原因是,mockito创建了间谍new Sut()
的浅表副本,而您的Supplier
则引用了原始Sut
实例中的已编译lambda方法,而不是间谍实例。
另请参阅this question和the mockito documentation。
调试代码时,您可以看到其工作原理:
Sut sut = spy(new Sut());
现在是实例Sut
的{{1}}的模拟/间谍子类。现在,原始TestTimes$Sut$MockitoMock$1381634547@5b202a3a
的所有字段都被浅层复制,包括new Sut()
。查看间谍内部的该字段,我们可以看到它是Supplier<Object> data
的一个实例,即指向原始TestTimes$Sut$$Lambda$1/510109769@1ecee32c
内的lambda。当我们在create方法中设置断点时,我们可以进一步观察到Sut
指向this
,即原始TestTimes$Sut@232a7d73
而不是间谍实例。
编辑:即使此MCVE可能与您的实际代码不相似,但还是想到以下解决方案:
Sut
(在构建过程中或作为Sut
的参数。getData
方法中懒惰地创建供应商(以使其指向模拟实例)getData