Spring Boot使用@WebMvcTest测试Controller时期望200却返回404
问题根本原因
你在测试类上额外添加的@ContextConfiguration注解覆盖了@WebMvcTest默认的上下文加载逻辑:
@WebMvcTest默认会自动注册你指定的TravelController到MVC上下文,同时只会加载Web层相关的Bean- 你手动配置的
@ContextConfiguration仅指定了Mapper相关的类,没有包含TravelController,导致控制器没有被Spring托管,对应的/travels接口也没有完成注册,请求自然返回404 - 你移除Mapper后不需要再添加
@ContextConfiguration注解,@WebMvcTest的默认逻辑生效,控制器正常注册,测试就能通过。
解决方案
方案1:移除@ContextConfiguration,用MockBean处理Mapper(Web层单元测试最优)
Web层单元测试不需要依赖真实的Mapper实现,直接Mock Mapper的返回结果即可:
@ExtendWith(SpringExtension.class) @WebMvcTest(controllers = TravelController.class) class TravelControllerTest { @Autowired private MockMvc mockMvc; @MockBean private TravelService travelService; // 将Mapper也改为MockBean @MockBean private TravelOutputDtoMapper travelOutputDtoMapper; @Test void testGetAll() throws Exception { List<Travel> travels = mockTravelList(); List<TravelOutputDto> mockDtoList = // 构造模拟的DTO返回结果 Mockito.when(travelService.getAll()).thenReturn(travels); // 补充Mapper的Mock逻辑 Mockito.when(travelOutputDtoMapper.travelToTravelOutputDto(any(Travel.class))) .thenAnswer(invocation -> { Travel travel = invocation.getArgument(0); return new TravelOutputDto(travel.getFrom().getCode(), travel.getTo().getCode()); }); mockMvc.perform(get("/travels")) .andExpect(status().isOk()); } private List<Travel> mockTravelList() { // 原有模拟逻辑保持不变 } }
方案2:保留真实Mapper实现,补充控制器到上下文配置
如果你的测试需要用到真实的MapStruct Mapper实现,只需把TravelController也加入@ContextConfiguration的类列表即可:
@ExtendWith(SpringExtension.class) @WebMvcTest(controllers = TravelController.class) @ContextConfiguration(classes = { TravelController.class, // 补充控制器类到上下文 TravelOutputDtoMapper.class, TravelOutputDtoMapperImpl.class }) class TravelControllerTest { // 其余代码和你原有逻辑保持一致即可 }
方案3:换用集成测试注解
如果测试需要加载更多上下文组件,可以直接用集成测试注解替换,无需手动配置上下文:
@SpringBootTest @AutoConfigureMockMvc class TravelControllerTest { // 其余代码和你原有逻辑保持一致即可 }
内容的提问来源于stack exchange,提问作者Kroxitrock
相关产品推荐
相关产品推荐

