Spring单元测试如何局部Mock依赖Bean不影响其他测试类
Spring单测试类局部Mock依赖实现方案
问题根因
- 原生
@Mock是Mockito提供的注解,只会在当前测试类实例化一个独立的Mock对象,不会替换Spring容器中已注册的真实Bean,因此ServiceA内部注入的还是容器原生的ServiceB实例,Mock逻辑自然不生效。 - 全局配置类上的
@TestConfiguration会被Spring测试组件扫描到,对所有测试类生效;旧版本@MockBean会修改Spring测试上下文缓存里的Bean实例,配置相同的其他测试类复用上下文时就会拿到被修改的Mock对象,产生跨测试类的影响。
可落地方案
方案1:内部静态类专属@TestConfiguration(无版本兼容问题,推荐)
不要把Mock配置放在全局可扫描的测试包路径下,而是在当前测试类内部定义静态的@TestConfiguration类,该配置仅对当前测试类生效,不会被其他测试类加载,天然不会产生全局污染。
@SpringBootTest class ServiceATest { @Autowired private ServiceA serviceA; @Autowired private ServiceB serviceB; // 内部静态配置仅当前测试类生效,其他测试类不会加载该配置 @TestConfiguration static class ServiceBMockConfig { @Bean @Primary // 保证当前上下文优先注入Mock的ServiceB,替换原有真实实例 ServiceB mockServiceB() { return Mockito.mock(ServiceB.class); } } @Test void testServiceAMethod() { // 对Mock对象打桩 Mockito.when(serviceB.queryData(any())).thenReturn("mockResult"); // 执行测试逻辑:ServiceA中注入的是Mock的ServiceB,ServiceC为容器中真实的实现类 String res = serviceA.executeBiz(); org.junit.jupiter.api.Assertions.assertEquals("expectVal", res); } }
方案2:@MockBean/@MockitoBean配合上下文清理(写法更简洁)
Spring Boot 3.4+版本提供的@MockitoBean、旧版本的@MockBean都可以直接声明要Mock的Bean,只需要在测试类上添加@DirtiesContext注解,指定当前测试类执行完成后销毁上下文,就可以避免修改后的上下文被其他测试类复用。
@SpringBootTest // 测试类执行完成后销毁当前上下文,不污染缓存中的其他上下文实例 @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) class ServiceATest { @Autowired private ServiceA serviceA; // 仅当前测试类的上下文中ServiceB会被替换为Mock实例 @MockitoBean // Spring Boot 3.4以下版本替换为@MockBean即可 private ServiceB serviceB; @Test void testServiceAMethod() { Mockito.when(serviceB.queryData(any())).thenReturn("mockResult"); // ServiceC自动使用容器中的真实实现,无需额外配置 String res = serviceA.executeBiz(); org.junit.jupiter.api.Assertions.assertEquals("expectVal", res); } }
避坑说明
- 禁止将Mock相关的
@TestConfiguration定义为公共类放在测试公共包、测试根目录下,否则会被组件扫描全局生效。 - 如果不使用
@DirtiesContext,内部静态@TestConfiguration的方案也不会污染其他测试:Spring测试缓存的上下文Key和测试类的专属配置绑定,其他测试类加载上下文时不会携带当前测试类的内部配置,会自动加载全新的、无Mock的原生上下文。 - 不需要额外对ServiceC做任何配置,Spring会自动注入真实的实现实例到ServiceA中。
内容的提问来源于stack exchange,提问作者Amirot
相关产品推荐
相关产品推荐

