Mockito.spy() 如何在添加间谍行为的同时返回相同的类型?

问题描述 投票:0回答:1

我怀疑

Mockito.spy()
返回参数对象副本的包装器,并且该包装器的类型是包装对象的生成子类型。那么间谍功能可能位于包装器的外部,就像通常在实现某些方面的代理对象的情况下一样。

不过,看起来好像不正确。包装器的类型与被包装对象的类型完全相同:

        List<String> list = new ArrayList<>();
        List<String> spyList = spy(list);

        assertThat(spyList.getClass()).isSameAs(list.getClass());

那么间谍功能是在哪里实现的呢?如果

spyList
ArrayList
那么 Mockito 将如何收到有关对它的不同调用的通知?

mockito wrapper spy
1个回答
0
投票

Mockito 通过使用重新转换(“HotSwap”)重写已加载类的字节码来实现这一点,因此 Mockito 甚至可以拦截

final
和系统类的行为。这是 5.0+ 版本中默认引入的新 MockitoMockMaker 的一个工件。这使得之前一些以子类为中心的 StackOverflow answers 有关 Mockito 内部结构的内容变得过时,尽管这些内容在 Android 和其他任何你不能指望仪器运行的地方仍然相关。

这个替代模拟生成器结合使用 Java 检测 API 和子类化,而不是创建一个新类来表示模拟。这样,就可以模拟最终类型和方法。

了解该实现的最佳位置是

org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker,而不是主 Javadoc 第 39 项底部链接的 org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker FQCN。该类顶级 Javadoc 的最后一段,重点是我的:

请注意,内联模拟需要附加 Java 代理。 Mockito 将尝试附加 Java 代理 加载模拟生成器以创建内联模拟。这种运行时附加只有在使用 JVM 时才可能实现 是 JDK 的一部分或使用 Java 9 VM 时。然而,当在 Java 9 之前的非 JDK VM 上运行时,可以 使用

-javaagent 手动添加 Byte Buddy Java 代理 jar

启动 JVM 时的参数。 
此外,内联模拟生成器需要VM支持类重新转换 (也称为 HotSwap)。 所有主要 VM 发行版,例如 HotSpot (OpenJDK)、J9 (IBM/Websphere) 或 Zing (Azul) 支持这个功能。

浏览 ByteBuddy 的

Advice javadoc 了解上下文,以及 Mockito 的 MockMethodAdviceMockMethodInterceptor 来了解 Mockito 如何分别在转换时和运行时实现这种魔力,也可能会有所帮助。


要重现此情况,请使用以下代码:

import static org.mockito.Mockito.spy; import java.util.ArrayList; import java.util.List; import org.mockito.Mockito; public class MyClass { public static void main(String args[]) { List<String> list = new ArrayList<>(); List<String> spyList = spy(list); System.out.println(list.getClass() + " " + list.getClass().hashCode()); System.out.println(spyList.getClass() + " " + spyList.getClass().hashCode()); System.out.println(list == spyList); System.out.println(list.getClass() == spyList.getClass()); System.out.println(Mockito.mockingDetails(list).isMock()); System.out.println(Mockito.mockingDetails(spyList).isMock()); } }
与 JDoodle 一样,在 Mockito 4.11.0 (org.mockito:mockito-core:4.11.0) 中运行,

此输出:

class java.util.ArrayList 1321659788 class org.mockito.codegen.ArrayList$MockitoMock$KpRNB6cF 257459516 false false false true
但是在 Mockito 5.10.0 (org.mockito:mockito-core:5.10.0) 上,

此输出:

class java.util.ArrayList 1321659788 class java.util.ArrayList 1321659788 false true false true
    
© www.soinside.com 2019 - 2024. All rights reserved.