You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 04:17:48