使用Mockito通过j.u.f.Supplier测试延迟初始化

时间:2019-06-26 21:07:07

标签: java mockito supplier

我有一个类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();
        }
    }
}

2 个答案:

答案 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();
        }
    }

我在调试时发现的东西

  1. Sut子句中正确调用了spy(new Sut())类构造函数,但是在此处没有调用create()方法。
  2. 每次调用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 questionthe 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
相关问题