Mockito如何验证特定枚举实例上的方法被正确调用
根因说明
Java枚举是JVM类加载阶段初始化的全局单例final类型,默认Mockito配置下,直接创建枚举spy、或是全量mock枚举类,都无法替换业务代码实际引用的原始枚举实例,自然无法拦截方法调用、完成verify校验。
实现方案
需要使用Mockito inline mock maker(支持静态方法、final类型mock),操作步骤:
- 开启inline mock能力:
- 方案1:直接引入
mockito-inline依赖(Mockito 3.4及以上版本内置支持) - 方案2:在测试资源路径创建
src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker文件,文件内容写入mock-maker-inline
- 方案1:直接引入
- 测试逻辑中通过
mockStatic拦截枚举实例的获取,将目标枚举实例替换为spy对象,存根paint方法跳过真实实现,最后完成调用校验。
测试代码示例(JUnit 5)
import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import static org.mockito.Mockito.*; public class ColorsPaintTest { @Test void verifyRedPaintInvokeWithExpectedCar() { Car testCar = mock(Car.class); // 用try-with-resources管控静态mock生命周期,避免上下文污染其他用例 try (MockedStatic<Colors> colorsStaticMock = mockStatic(Colors.class)) { // 为RED实例创建spy,存根paint方法不执行真实逻辑 Colors redSpy = spy(Colors.RED); doNothing().when(redSpy).paint(any(Car.class)); // 拦截Colors.RED的返回,替换为spy实例 colorsStaticMock.when(() -> Colors.RED).thenReturn(redSpy); // 此处执行被测业务代码,例如逻辑中包含Colors.RED.paint(car)调用 // demo: yourBusinessComponent.paintCarRed(testCar); // 校验:RED的paint方法被调用1次,入参为指定的testCar verify(redSpy, times(1)).paint(testCar); } } }
注意事项
- 必须手动关闭
MockedStatic实例:JUnit5用try-with-resources自动回收,JUnit4需要在@After方法中手动close,否则静态mock上下文会泄漏,干扰其他测试用例执行。 - 存根顺序不能乱:先完成spy对象的方法存根,再设置
Colors.RED返回spy实例,避免业务逻辑提前拿到原始枚举实例导致拦截失效。 - 如果业务代码可重构,更优实践是将颜色能力抽象为普通接口,枚举实现该接口,业务层通过依赖注入传入颜色实例,后续测试直接使用普通mock即可,无需依赖静态mock能力。
内容的提问来源于stack exchange,提问作者Andreas Dolk
相关产品推荐
相关产品推荐

