基于JUnit 5和Mockito的Spring WebFlux控制器单元测试问题
问题描述
我有服务类和控制器类,现在在控制器的单元测试里,想验证学生已存在的场景,但测试收到的是异常而非ResponseEntity。请问如何修改测试,在控制器层直接验证409状态码?我认为控制器单元测试只需验证返回的状态码,服务层测试负责验证模型值是否正确。
控制器代码
@PostMapping("/save") public Mono<ResponseEntity<Alumno>> saveAlumno(@Valid @RequestBody Mono<Alumno> alumno) { return alumno.flatMap(alum -> alumnoService.saveAlumno(alum) .then(Mono.just(ResponseEntity.status(HttpStatus.CREATED).build()))); } @Test void returnHttpConflictWhenStudentExists() { when(alumnoService.saveAlumno(any())) .thenThrow(new AlumnoExistenteException("Student exists")); StepVerifier.create(alumnoController.saveAlumno(Mono.just(TestUtils.mockNewAlumno()))) .expectErrorMessage("Student Exists") .verify(); }
服务层代码
@Override public Mono<Void> saveAlumno(Alumno alumno) { return alumnoRepository.existsById(alumno.getId()) .flatMap(alumnoExiste -> { if(alumnoExiste) { log.info("<<< student exists >>>"); return Mono.error(new AlumnoExistenteException("Student Exists")); } log.info("<<< student doesnt exist >>>"); return alumnoRepository.save(alumno).then(); }); } @Test void throwsAlumnoExistenteException() { when(alumnoRepository.existsById(anyString())).thenReturn(Mono.just(true)); StepVerifier.create(alumnoService.saveAlumno(TestUtils.mockNewAlumno())) .expectErrorMatches(throwable -> throwable instanceof AlumnoExistenteException) .verify(); }
解决方案
要在控制器层返回409状态码并验证,需要两步操作:
1. 让控制器处理服务抛出的异常
当前控制器没有捕获服务层抛出的AlumnoExistenteException,导致异常直接向上传播,无法返回标准的ResponseEntity。可以通过两种方式处理:
方式一:在控制器方法内添加异常处理
修改控制器的saveAlumno方法,用onErrorResume捕获指定异常,返回409状态码的响应:
@PostMapping("/save") public Mono<ResponseEntity<Alumno>> saveAlumno(@Valid @RequestBody Mono<Alumno> alumno) { return alumno.flatMap(alum -> alumnoService.saveAlumno(alum) .then(Mono.just(ResponseEntity.status(HttpStatus.CREATED).build()))) // 捕获学生已存在的异常,返回409冲突 .onErrorResume(AlumnoExistenteException.class, e -> Mono.just(ResponseEntity.status(HttpStatus.CONFLICT).build())); }
方式二:全局异常处理器(推荐)
创建全局异常处理类,统一处理这类业务异常,避免每个控制器方法重复写异常处理逻辑:
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(AlumnoExistenteException.class) public Mono<ResponseEntity<String>> handleAlumnoExistenteException(AlumnoExistenteException e) { return Mono.just(ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage())); } }
2. 修改控制器单元测试验证409状态码
现在控制器会返回包含409状态码的ResponseEntity,测试可以直接验证响应的状态:
@Test void returnHttpConflictWhenStudentExists() { when(alumnoService.saveAlumno(any())) .thenThrow(new AlumnoExistenteException("Student exists")); StepVerifier.create(alumnoController.saveAlumno(Mono.just(TestUtils.mockNewAlumno()))) // 验证返回的ResponseEntity状态码是409 .expectNextMatches(response -> response.getStatusCode() == HttpStatus.CONFLICT) .verifyComplete(); }
这样就能在控制器单元测试中直接验证409状态码,符合你“控制器测状态码、服务层测业务逻辑”的分工原则。
内容的提问来源于stack exchange,提问作者DarkVaderM
相关产品推荐
相关产品推荐

