You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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());
    }
}

排查常见坑

  1. 确保所有类都被Spring托管:SomeClass、SomeOtherClass、YetAnotherClass必须加@Service/@Component等注解,否则Spring不会自动注入依赖。
  2. 避免多个Bean冲突:如果原YetAnotherClass是@Primary,@MockBean依然会替换它,但如果有多个同类型Bean,需要明确指定要替换的Bean(比如用@MockBean(name = "yetAnotherClass"))。
  3. 上下文缓存问题:如果多个测试类共享上下文,可能导致Mock没被刷新,可以加@DirtiesContext强制刷新上下文(不推荐频繁用,影响测试速度)。

内容的提问来源于stack exchange,提问作者Hossein

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:30:01