Mockito:在功能接口内监视函数调用?

Mockito: Spying function calls inside Functional interfaces?

Mockito 似乎无法监视函数式接口内的函数调用。假设我有一个带有服务的简单 Spring 启动应用程序:

@Service
public class TestService {

    Function<Integer, Integer> mapping = this::add2;

    Integer add2(Integer integer) {
        return integer + 2;
     }

}

还有一个测试:

@SpyBean
TestService testService;

@Test
public void mockitoTest2(){

    doReturn(6).when(testService).add2(2);

    System.out.println(testService.mapping.apply(2));

}

测试将 return 4 而不是 6。这是预期的,还是值得报告错误?

这是意料之中的。 Mockito 通过制作 浅拷贝 创建间谍,方法引用 this::add2 被复制,同时保留对旧 this.

的引用
TestService myTestService = new TestService();
TestService mySpy = Mockito.spy(myTestService);

在此示例中,mySpyTestService 的生成子类的一个实例,它的所有可重写方法都被重写以委托给 Mockito,并且所有它的实例状态是从 myTestService 浅层复制而来的。这意味着myTestService.mapping == mySpy.mapping,这也意味着在函数中捕获对this(意思是myTestService)的引用被复制过来。

应用于实例的方法引用捕获该实例,如 "Kinds of Method References" 下的 the Oracle page on Method References。接收 add2 调用的对象是原始对象,而不是间谍,因此您得到原始行为 (4) 而不是受间谍影响的行为 (6)。

这应该有点直观:您可以调用 Function<Integer, Integer> 而无需传递 TestService 实例,因此 Function 包含对 TestService 实现的隐式引用是非常合理的。您看到此行为是因为间谍实例的状态是从真实实例复制的 函数初始化并存储 this 之后。


考虑这个替代方案,您可以在 TestService 上定义它:

BiFunction<TestService, Integer, Integer> mapping2 = TestService::add2;

这里,函数 mapping2 不应用于特定对象,而是应用于传入的任何 TestService 实例。因此,您的测试将调用此:

@Test
public void mockitoTest2(){
    doReturn(6).when(testService).add2(2);
    System.out.println(testService.mapping2.apply(testService, 2));
}

...并且因为您正在传递您的间谍 testService,它将处理对 add2 的虚拟方法调用并调用间谍上设置的行为(返回 6)。没有隐式保存 this,因此您的函数按预期工作。

另请参阅: