集成测试中Mockito验证接口实现类调用失败问题排查
解决集成测试中Mockito验证Bean调用失败的问题
问题根源
批量执行Maven测试时,Spring会复用上下文,导致@SpyBean生成的代理Bean和ServiceHandler中注入的接口实现类列表里的实例不是同一个。单独跑测试时上下文是全新创建的,SpyBean能正确替换原Bean;但批量跑时,原Bean已经存在于复用的上下文中,代理实例没被注入到列表里,所以Mockito监听不到调用。
可行解决方案
1. 强制重新加载测试上下文
给测试类加上@DirtiesContext注解,确保每次运行该测试都重新生成Spring上下文,避免复用其他测试的上下文导致实例不一致:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) class ServiceHandlerTest { @SpyBean private InterfaceImpl serviceImpl; @Autowired private ServiceHandler serviceHandler; @Test void test() { serviceHandler.handler(); verify(serviceImpl, times(1)).call(); } }
2. 通过接口类型注入SpyBean
不要直接注入实现类,改用接口类型注入SpyBean,这样ServiceHandler的列表里能拿到代理后的实例:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") class ServiceHandlerTest { @SpyBean private Interface serviceImpl; @Autowired private ServiceHandler serviceHandler; @Test void test() { serviceHandler.handler(); verify(serviceImpl, times(1)).call(); } }
3. 用@MockBean替换原Bean(无需真实逻辑时)
如果测试不需要原Bean的真实业务逻辑,直接用@MockBean替换原Bean,确保调用的是Mock实例:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ActiveProfiles("test") class ServiceHandlerTest { @MockBean private InterfaceImpl serviceImpl; @Autowired private ServiceHandler serviceHandler; @Test void test() { when(serviceImpl.integrationServiceType()).thenReturn(Enum.Service); serviceHandler.handler(); verify(serviceImpl, times(1)).call(); } }
验证实例一致性的小技巧
可以在测试里打印实例哈希值,确认ServiceHandler列表中的Bean和SpyBean是否为同一个:
@Test void checkBeanConsistency() { Interface serviceFromList = serviceHandler.getServices().stream() .filter(s -> s.integrationServiceType().equals(Enum.Service)) .findFirst().orElseThrow(); System.out.println("列表中的实例哈希:" + serviceFromList.hashCode()); System.out.println("SpyBean实例哈希:" + serviceImpl.hashCode()); }
内容的提问来源于stack exchange,提问作者Felipe Xavier
相关产品推荐
相关产品推荐

