Spring Boot测试:Mock责任链深层的Handler Bean
解决Spring Boot深层Bean Mock问题(Mockito实现)
我之前也碰到过类似的情况,明明加了@MockBean但深层依赖没被替换,大概率是测试上下文的配置或者注解使用不对。下面给你几个靠谱的解决方案,都是用纯Mockito+Spring Boot测试注解实现的:
方案1:用@MockBean配合完整Spring上下文(集成测试)
这是最常用的集成测试方式,只要保证测试类正确加载上下文,@MockBean会自动替换掉Spring容器里的原Bean,整个依赖链都会注入Mock实例。
代码示例:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) @AutoConfigureMockMvc class YourRestControllerTest { @Autowired private MockMvc mockMvc; // 直接Mock最底层的YetAnotherClass,Spring会自动把这个Mock注入到依赖链中 @MockBean private YetAnotherClass yetAnotherClassMock; @Test void testRestApiBehavior() throws Exception { // 先定义Mock的预期行为 when(yetAnotherClassMock.yourMethod(anyString())).thenReturn("Mocked Result"); // 发送REST请求 mockMvc.perform(get("/your-api-path") .contentType(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()); // 验证Mock方法是否被调用 verify(yetAnotherClassMock, times(1)).yourMethod(anyString()); } }
为什么之前没生效?
如果你之前用了@WebMvcTest而不是@SpringBootTest,要注意@WebMvcTest默认只加载Controller层的Bean,Service层的SomeClass、SomeOtherClass不会被自动扫描。这时候需要手动导入这些类:
@WebMvcTest(YourRestController.class) @Import({SomeClass.class, SomeOtherClass.class}) // 导入依赖链中的类 class YourRestControllerTest { // 同样用@MockBean定义YetAnotherClass的Mock @MockBean private YetAnotherClass yetAnotherClassMock; // ... 测试代码同上 }
方案2:用@TestConfiguration手动替换Bean
如果你想用自定义配置来替换Bean,要注意@TestConfiguration的类必须是静态内部类,否则Spring无法正确加载并覆盖原Bean。
代码示例:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK) @AutoConfigureMockMvc class YourRestControllerTest { @Autowired private MockMvc mockMvc; @Autowired private YetAnotherClass yetAnotherClassMock; // 注入我们自定义的Mock Bean // 静态内部类定义测试配置 @TestConfiguration static class TestConfig { @Bean public YetAnotherClass yetAnotherClass() { // 用Mockito创建Mock实例 return Mockito.mock(YetAnotherClass.class); } } @Test void testRestApi() throws Exception { when(yetAnotherClassMock.yourMethod(any())).thenReturn("Mocked Data"); mockMvc.perform(get("/your-api-path")) .andExpect(status().isOk()); verify(yetAnotherClassMock).yourMethod(any()); } }
方案3:纯Mockito单元测试(不加载Spring上下文)
如果只是想测试Controller的逻辑,不需要完整Spring上下文,可以用Mockito的@InjectMocks手动构建依赖链:
代码示例:
class YourRestControllerUnitTest { private MockMvc mockMvc; // 定义最底层的Mock @Mock private YetAnotherClass yetAnotherClassMock; // 用@InjectMocks自动注入依赖:SomeOtherClass会注入yetAnotherClassMock,以此类推 @InjectMocks private SomeOtherClass someOtherClass; @InjectMocks private SomeClass someClass; @InjectMocks private YourRestController yourRestController; @BeforeEach void setUp() { // 初始化Mockito注解 MockitoAnnotations.openMocks(this); // 构建MockMvc mockMvc = MockMvcBuilders.standaloneSetup(yourRestController).build(); } @Test void testControllerLogic() throws Exception { when(yetAnotherClassMock.yourMethod(anyString())).thenReturn("Mocked Result"); mockMvc.perform(get("/your-api-path")) .andExpect(status().isOk()); verify(yetAnotherClassMock).yourMethod(anyString()); } }
排查常见坑
- 确保所有类都被Spring托管:
SomeClass、SomeOtherClass、YetAnotherClass必须加@Service/@Component等注解,否则Spring不会自动注入依赖。 - 避免多个Bean冲突:如果原
YetAnotherClass是@Primary,@MockBean依然会替换它,但如果有多个同类型Bean,需要明确指定要替换的Bean(比如用@MockBean(name = "yetAnotherClass"))。 - 上下文缓存问题:如果多个测试类共享上下文,可能导致Mock没被刷新,可以加
@DirtiesContext强制刷新上下文(不推荐频繁用,影响测试速度)。
内容的提问来源于stack exchange,提问作者Hossein
相关产品推荐
相关产品推荐

