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

使用@SpringBootTest时如何模拟嵌套Bean?Spring Boot集成测试依赖Mock与上下文加载问题求助

解决Spring Boot集成测试中Mock嵌套依赖的问题

看起来你遇到了一个很典型的Spring Boot测试场景:想轻量测试Controller和Service的业务逻辑,但又要Mock掉Dao/Repo和Feign这些外部依赖,同时避免加载完整上下文导致测试变慢。让我们一步步解决这个问题。

问题根源

你用@WebMvcTest时出现NoSuchBeanDefinitionException,是因为**@WebMvcTest默认只加载Web层的Bean**(比如@RestController、@Controller),不会自动扫描和加载@Service注解的类。所以你的TokenController依赖的TokenService在上下文中找不到,导致启动失败。

解决方案1:用@WebMvcTest + @Import + @MockBean(推荐轻量方案)

这个方案会只加载Web层和你指定的Service,同时Mock掉Service依赖的外部组件(Dao/Feign),完全符合你的需求:

@Slf4j
@WebMvcTest(TokenController.class) // 明确指定要测试的Controller,缩小上下文范围
@Import(TokenService.class) // 手动导入需要测试的Service类
public class TokenControllerTest {
    @Autowired
    private MockMvc mockMvc;
    
    @MockBean // 用Mock替换Spring上下文中的AppDao实例
    private AppDao appDao;
    
    @Autowired // 现在可以正常注入实际的TokenService,它内部的AppDao会是Mock实例
    private TokenService tokenService;
    
    private ObjectMapper mapper = new ObjectMapper();

    @Test
    public void givenAppNameAndSecret_shouldReturnWithToken() throws Exception {
        AppTokenDTO tokenDTO = AppTokenDTO.builder()
                .appName("dummy")
                .accessToken("uoqiuwe90273492308019238472839423")
                .expiresIn(1800L)
                .build();
        App app = AppStub.getApp();
        
        // 配置Mock的行为
        when(appDao.getApp(app.getName(), app.getApiKey())).thenReturn(app);
        
        mockMvc.perform(MockMvcRequestBuilders.post("/tokens")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(mapper.writeValueAsString(app)))
                .andDo(MockMvcResultHandlers.print())
                .andExpect(MockMvcResultMatchers.status().isOk())
                .andExpect(MockMvcResultMatchers.jsonPath("$.data.accessToken").isNotEmpty());
    }
}

关键说明:

  • @WebMvcTest(TokenController.class):只加载与TokenController相关的Web层Bean,避免加载其他无关的Controller。
  • @Import(TokenService.class):把你的业务Service手动加入测试上下文,这样Controller可以正常注入它。
  • @MockBean private AppDao appDao:Spring会自动把这个Mock实例替换掉TokenService中依赖的AppDao,确保不会执行实际SQL。
  • 如果你的TokenService还依赖Feign客户端,只需要再添加一个@MockBean private YourFeignClient feignClient即可,原理一样。

解决方案2:用@SpringBootTest + webEnvironment = MOCK(折中方案)

如果你觉得@WebMvcTest的配置太受限,也可以用@SpringBootTest但指定webEnvironment = SpringBootTest.WebEnvironment.MOCK,这样不会启动嵌入式服务器,比默认的全上下文模式更快,同时依然可以用@MockBean替换外部依赖:

@Slf4j
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@AutoConfigureMockMvc
public class TokenControllerTest {
    @Autowired
    private MockMvc mockMvc;
    
    @MockBean
    private AppDao appDao;
    
    @Autowired
    private TokenService tokenService;
    
    private ObjectMapper mapper = new ObjectMapper();

    // 测试方法不变
}

这个方案的上下文范围比@WebMvcTest大,但比默认的@SpringBootTest轻量,适合Service依赖较多、@Import起来麻烦的场景。

为什么之前的@SpringBootTest慢?

默认的@SpringBootTest会加载完整的应用上下文,包括数据库连接池、Feign客户端的实际配置等,所以启动慢。而上面两种方案都避免了加载这些不必要的组件,同时通过@MockBean隔离了外部依赖。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 18:52:41