测试RestController Delete方法返回404(预期400),测试方法异常
shouldNotDeletePersonByGivenId返回404而非预期400的问题 你的测试出现预期状态码不匹配的问题,我帮你梳理几个核心排查方向和对应的解决办法:
1. 先确认控制器路径是否匹配
404状态码最直接的原因是请求路径找不到对应的控制器方法,你需要检查:
- 你的Person控制器类上是否标注了
@RestController+@RequestMapping("/persons") - 删除方法是否标注了
@DeleteMapping("/{id}")(如果类上已有@RequestMapping("/persons"),方法上的@DeleteMapping可以直接写@DeleteMapping或@DeleteMapping("/{id}"),确保和测试中的/persons/{id}路径完全匹配)
如果路径不匹配,请求会直接返回404,和服务层的异常逻辑无关。
2. 检查控制器删除逻辑是否提前拦截了请求
如果控制器在调用服务层deleteById之前,自行做了实体存在性检查并返回404,那你的mock异常逻辑根本不会被触发。比如这种情况:
@DeleteMapping("/{id}") public ResponseEntity<Void> deletePerson(@PathVariable Long id) { // 提前检查实体,不存在直接返回404 Person person = personService.findById(id); if (person == null) { return ResponseEntity.notFound().build(); } personService.deleteById(id); return ResponseEntity.noContent().build(); }
如果是这种情况,你需要修改控制器逻辑,把实体存在性检查交给服务层的deleteById方法处理(让deleteById在找不到实体时抛出NoEntityFoundException),控制器只负责调用服务层方法即可:
@DeleteMapping("/{id}") public ResponseEntity<Void> deletePerson(@PathVariable Long id) { personService.deleteById(id); return ResponseEntity.noContent().build(); }
3. 配置异常映射,将自定义异常关联到400状态码
Spring默认不会把自定义RuntimeException自动映射为400 Bad Request,通常会返回500 Internal Server Error。你需要添加异常处理逻辑来指定这个异常对应的状态码:
方案一:全局异常处理器(推荐,适用于所有控制器)
创建一个全局异常处理类,捕获NoEntityFoundException并返回400:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(PersonService.NoEntityFoundException.class) public ResponseEntity<String> handleNoEntityFoundException(PersonService.NoEntityFoundException ex) { return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); } }
方案二:控制器局部异常处理
如果只需要在当前控制器处理这个异常,可以在Person控制器中添加@ExceptionHandler方法:
@RestController @RequestMapping("/persons") public class PersonController { // 注入personService... @DeleteMapping("/{id}") public ResponseEntity<Void> deletePerson(@PathVariable Long id) { personService.deleteById(id); return ResponseEntity.noContent().build(); } @ExceptionHandler(PersonService.NoEntityFoundException.class) public ResponseEntity<String> handleNoEntityFound(PersonService.NoEntityFoundException ex) { return ResponseEntity.badRequest().body(ex.getMessage()); } }
4. 验证Mock的参数匹配是否正确
检查你的mock语句:
Mockito.doThrow(new PersonService.NoEntityFoundException()) .when(personService).deleteById(1L);
这里你mock的是deleteById(1L)(Long类型的1),而测试请求传入的是1(Integer类型)。如果控制器的@PathVariable参数是Long id,Spring会自动把Integer的1转为Long的1L,参数匹配没问题;但如果控制器参数是Integer id,服务层deleteById接收的是Integer类型,你的mock就不匹配(因为mock的是Long类型参数),此时服务层不会抛出异常,可能导致控制器返回非预期状态码。
确保控制器路径参数类型和服务层方法参数类型一致即可。
按照上面的步骤排查后,你的测试应该就能正确返回400状态码了。
内容的提问来源于stack exchange,提问作者Magdalena Rumakowicz

