Spring Boot:添加Authorization头后MockMVC单元测试返回404
问题排查:无效JWT带Bearer前缀返回404的原因及修复
核心现象
测试无效JWT场景时:
- 未添加Bearer前缀:请求被Spring Security拦截,返回401(符合预期)
- 添加Bearer前缀:请求返回404(不符合预期,应为401)
关键原因分析
- MockMvc构建逻辑错误:测试类中使用
standaloneSetup(context)传入WebApplicationContext,而standaloneSetup设计用于单独测试控制器实例,传入上下文会导致路由映射未正确加载,请求无法匹配到控制器接口。 - 控制器路由前缀缺失:若BookingController类未添加
@RequestMapping("/booking")前缀,方法上的@PostMapping("")实际对应根路径/,但测试请求的是/booking,导致路径不匹配返回404。 - 测试逻辑混淆:Mock的
bookingService抛出异常的逻辑不会被执行,因为请求根本没到达控制器层(404说明路由未匹配)。
修复步骤
1. 修正MockMvc构建方式
将standaloneSetup(context)改为传入控制器实例,确保路由映射正确加载:
@BeforeEach public void setup() { this.mockMvc = MockMvcBuilders.standaloneSetup(bookingController) .setControllerAdvice(new GeneralBookingException(), new UnauthorizedException()) .apply(springSecurity(springSecurityFilterChain)) .build(); }
若需基于WebApplicationContext测试,改用webAppContextSetup(需配合@MockBean替换服务层):
@BeforeEach public void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(context) .apply(springSecurity(springSecurityFilterChain)) .build(); }
2. 补充控制器路由前缀
确认BookingController类上存在@RequestMapping("/booking")注解,保证请求路径匹配:
@RestController @RequestMapping("/booking") public class BookingController { // 现有方法代码 @Operation(summary = "Booking Creation") @PostMapping("") public Mono<BookingResponse> userBooking(...) { // ... } }
3. 调整测试类注解(针对webAppContextSetup场景)
若使用webAppContextSetup,需用@MockBean替代@Mock和@InjectMocks,让Spring自动管理上下文和Mock实例:
@SpringBootTest @AutoConfigureMockMvc class BookingControllerTest { @Autowired private MockMvc mockMvc; @MockBean private BookingService bookingService; // 测试方法保持不变 }
4. 验证JWT无效场景的正确逻辑
当修复路由问题后,无效JWT(带Bearer前缀)会被Spring Security的JWT解码器识别为无效,直接返回401,无需依赖服务层抛出异常。可调整测试逻辑,无需Mock服务层:
@Test public void testUserBooking_Unauthorized() throws Exception { BookingCreateRequestDTO request = new BookingCreateRequestDTO(); request.setClientId("client1"); mockMvc.perform(post("/booking") .contentType(MediaType.APPLICATION_JSON) .header("Authorization", "Bearer invalid-token") .content(objectToJson(request))) .andExpect(status().isUnauthorized()); }
内容的提问来源于stack exchange,提问作者MetaCoder
相关产品推荐
相关产品推荐

