使用@SpringBootTest时如何模拟嵌套Bean?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

