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

Spring WebFlux API层单元测试返回404问题求助

Spring WebFlux + OpenAPI Generator 单元测试返回404问题

我基于Spring Boot 3.0学习Spring WebFlux,使用OpenAPI Generator构建Person API。手动测试应用时能得到预期结果,但API层的单元测试始终返回404状态码,修改过Context Configuration也无法解决问题,求帮助。

测试类

@WebFluxTest(controllers = {PersonApiController.class})
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {PersonMapperImpl.class, H2PersonRepository.class, PersonRepository.class})
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class PersonRouterTest {

    @MockBean
    private PersonService personService;

    @Autowired
    private WebTestClient client;

    @ParameterizedTest
    @CsvSource({"1234, Max Mustermann", "5678, Erika Musterfrau"})
    void retrieve_a_name(String id, String name) {
        when(personService.getPersonDataByID(1234)).thenReturn(Mono.just(new PersonData(1234, "Max Mustermann")));
        when(personService.getPersonDataByID(5678)).thenReturn(Mono.just(new PersonData(5678, "Erika Musterfrau")));

        client.get()
                .uri(uriBuilder -> uriBuilder
                        .path("/persons/{id}")
                        .build(id))
                .accept(MediaType.ALL)
                .exchange()
                .expectStatus().isOk()
                .expectHeader().contentType(MediaType.APPLICATION_JSON)
                .expectBody()
                .jsonPath("$.name").isEqualTo(name);
    }
}

控制器类

@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2022-12-09T09:14:36.692713900+01:00[Europe/Vienna]")
@Controller
@RequestMapping("${openapi.openAPIDefinition.base-path:}")
public class PersonApiController implements PersonApi {

    private final PersonApiDelegate delegate;

    public PersonApiController(@Autowired(required = false) PersonApiDelegate delegate) {
        this.delegate = Optional.ofNullable(delegate).orElse(new PersonApiDelegate() {});
    }

    @Override
    public PersonApiDelegate getDelegate() {
        return delegate;
    }

}

API接口

@Tag(
    name = "Person",
    description = "the Person API"
)
public interface PersonApi {
    default PersonApiDelegate getDelegate() {
        return new PersonApiDelegate() {
        };
    }

    @Operation(
        operationId = "findPersonById",
        summary = "Find Person by ID",
        tags = {"Person"},
        responses = {@ApiResponse(
    responseCode = "200",
    description = "successful operation",
    content = {@Content(
    mediaType = "application/json",
    schema = @Schema(
    implementation = PersonData.class
)
)}
)}
    )
    @RequestMapping(
        method = {RequestMethod.GET},
        value = {"/persons/{id}"},
        produces = {"application/json"}
    )
    default Mono<ResponseEntity<PersonData>> findPersonById(@Parameter(name = "id",description = "Person ID",required = true) @PathVariable("id") Integer id, @Parameter(hidden = true) final ServerWebExchange exchange) {
        return this.getDelegate().findPersonById(id, exchange);
    }

    @Operation(
        operationId = "savePerson",
        summary = "Creates a new Person",
        tags = {"Person"},
        responses = {@ApiResponse(
    responseCode = "200",
    description = "successful operatoin",
    content = {@Content(
    mediaType = "application/json",
    schema = @Schema(
    implementation = PersonData.class
)
)}
)}
    )
    @RequestMapping(
        method = {RequestMethod.POST},
        value = {"/persons"},
        produces = {"application/json"},
        consumes = {"application/json"}
    )
    default Mono<ResponseEntity<PersonData>> savePerson(@Parameter(name = "PersonData",description = "") @RequestBody(required = false) Mono<PersonData> personData, @Parameter(hidden = true) final ServerWebExchange exchange) {
        return this.getDelegate().savePerson(personData, exchange);
    }
}

Delegate实现类

@Service
public class PersonDelegateImpl implements PersonApiDelegate {

    public static final Mono<ResponseEntity<?>> RESPONSE_ENTITY_MONO = Mono.just(ResponseEntity.notFound().build());

    private final PersonService service;
    private final PersonMapper mapper;

    public PersonDelegateImpl(PersonService service, PersonMapper mapper) {
        this.service = service;
        this.mapper = mapper;
    }

    public static <T> Mono<ResponseEntity<T>> toResponseEntity(Mono<T> mono) {
        return mono.flatMap(t -> Mono.just(ResponseEntity.ok(t)))
                .onErrorResume(t -> Mono.just(ResponseEntity.internalServerError().build()));

    }

    @Override
    public Mono<ResponseEntity<PersonData>> findPersonById(Integer id, ServerWebExchange exchange) {
        Mono<com.ebcont.talenttoolbackend.person.PersonData> personDataByID = service.getPersonDataByID(id);
        return toResponseEntity(personDataByID.map(mapper::map));
    }

    @Override
    public Mono<ResponseEntity<PersonData>> savePerson(Mono<PersonData> personData, ServerWebExchange exchange) {
        return PersonApiDelegate.super.savePerson(personData, exchange);
}

测试报错信息

< 404 NOT_FOUND Not Found
< Content-Type: [application/json]
< Content-Length: [139]

{"timestamp":"2022-12-09T08:45:41.278+00:00","path":"/persons/1234","status":404,"error":"Not Found","message":null,"requestId":"4805b8b8"}

内容的提问来源于stack exchange,提问作者Christoph Kronberger

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 11:35:13