使用MockMVC测试RestController接口时预期异常未正常捕获问题
问题1:传入null参数接口收到字符串"null"的成因
你做URL拼接时使用了代码get("/brands?brandId="+brandId_bad_req),当brandId_bad_req为null时,Java的字符串拼接逻辑会自动把null对象转换为字符串"null",最终发起的请求实际是/brands?brandId=null,所以接口收到的参数值就是字符串类型的"null",不是空值。
要传真正的空参数,直接不拼接brandId参数即可,或者用MockMvc提供的param方法传null值:
mockMvc.perform(get("/brands").param("brandId", (String) null))
问题2:传入空字符串抛出异常后测试中断的成因
你自定义的ApiException没有被全局异常处理器捕获处理,Spring MVC默认会把控制器抛出的未捕获异常包装为NestedServletException直接抛出,不会自动转换成你预期的400状态码响应,所以测试执行到抛出异常的步骤就直接中断,不会走到后面的状态码断言逻辑。
解决方法可以二选一:
- 添加全局异常处理器,捕获
ApiException并返回对应状态码的响应:
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(ApiException.class) public ResponseEntity<ErrorInfo> handleApiException(ApiException e) { ErrorInfo errorInfo = new ErrorInfo(e.getErrorMessage(), e.getErrorCode(), e.getTimestamp()); return new ResponseEntity<>(errorInfo, HttpStatus.valueOf(e.getStatus().value())); } }
- 测试侧直接对抛出的异常做断言:
mockMvc.perform(get("/brands?brandId=")) .andExpect(status().isBadRequest()) .andExpect(result -> assertTrue(result.getResolvedException() instanceof ApiException));
问题3:requestAttr传参测试一直通过的成因
requestAttr方法是往请求域(Request Attribute)里设置属性,而@RequestParam注解是读取URL后的查询参数(Query Parameter),二者完全不是一个东西。你用requestAttr设置brandId的时候,实际请求的查询参数里根本没有brandId这个字段,触发了Spring MVC框架层面对@RequestParam(required = true)的校验,直接返回400状态码,和你控制器里写的校验逻辑没有任何关系,所以不管你注不注释控制器里的校验代码,这个断言都会通过,不符合你测试自定义校验逻辑的预期。
正确的传查询参数的写法是用param方法,或者直接拼接在URL上:
// 正确写法1 mockMvc.perform(get("/brands").param("brandId", "")) .andExpect(status().isBadRequest()); // 正确写法2 mockMvc.perform(get("/brands?brandId=")) .andExpect(status().isBadRequest());
内容的提问来源于stack exchange,提问作者jaco

