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

使用RestAssured测试Quarkus响应式API时Uni序列化异常解决

问题:Quarkus响应式API测试时RestAssured序列化Uni失败

在Quarkus应用中使用RestAssured测试基于SmallRye Mutiny开发的响应式Controller时,遇到Jackson序列化异常,提示无法找到Uni类型的序列化器:

Caused by:
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of 
`io.smallrye.mutiny.Uni` (no Creators, like default constructor, exist): abstract types either 
need to be mapped to concrete types, have custom deserializer, or contain additional type 
information

at [Source: (String) 
{   
   "header":{ 
      "statusCode":"99-9999",
      "statusDescription":"com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class io.smallrye.mutiny.operators.uni.builders.UniCreateFromKnownItem and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)"    
    } 
} 
; line: 1, column: 1]

代码实现

服务层代码

import io.smallrye.mutiny.Uni;
@ApplicationScoped
public class AccountOpeningServiceImpl implements AccountOpeningService {

    @Override
    public Uni<GetRandomNumberResponseApi> getRandomNumber(GetRandomNumberRequestApi requestApi) {
       Uni<GetRandomNumberResponseApi> numberUni = Uni.createFrom().item(() -> GetRandomNumberResponseApi.builder().number(10).build());
       return numberUni;
    }
}

控制器代码

import io.smallrye.mutiny.Uni;    
@Path("")
public class AccountOpeningController {

    @Inject
    AccountOpeningService accountOpeningService;

    @POST
    @Path("/random-number")
    public Uni<GetRandomNumberResponseApi> getRandomNumber(@Valid GetRandomNumberRequestApi requestApi) {
        return accountOpeningService.getRandomNumber(requestApi);
    }
}

测试代码

@QuarkusTest
public class AccountOpeningControllerTest {

    @InjectMock
    AccountOpeningService accountOpeningService;

    @Inject
    MockUtil mockUtil;

    @Test
    void getRandomNumber() throws IOException {
        GetRandomNumberRequestApi requestApi = mockUtil.getMock("Get_Random_Number_Request.json",
                GetRandomNumberRequestApi.class);

        GetRandomNumberResponseApi responseApi = mockUtil.getMock("Get_Random_Number_Response.json",
                GetRandomNumberResponseApi.class);
        when(accountOpeningService.getRandomNumber(requestApi)).thenReturn(Uni.createFrom().item(responseApi));

        Uni<GetRandomNumberResponseApi> response = given()
                .contentType(MediaType.APPLICATION_JSON)
                .body(requestApi)
                .when()
                .post("/get-random-number")
                .then()
                .statusCode(200)
                .extract()
                .as(new TypeRef<Uni<GetRandomNumberResponseApi>>() {
                });
        
        GetRandomNumberResponseApi actualResponse = response.subscribe()
                .withSubscriber(UniAssertSubscriber.create())
                .assertCompleted()
                .getItem();
        Assertions.assertEquals(responseApi, actualResponse);
    }
}

解决方法

1. 修正测试的响应提取逻辑

问题核心是Quarkus框架会自动订阅Uni并将实际的响应体(GetRandomNumberResponseApi)返回给客户端,RestAssured接收到的是实际业务对象的JSON,而非Uni对象的JSON。因此测试中不需要将响应提取为Uni类型,直接提取成业务对象即可:

修改测试代码中的提取逻辑:

// 替换原有的extract().as(...)代码段
GetRandomNumberResponseApi actualResponse = given()
        .contentType(MediaType.APPLICATION_JSON)
        .body(requestApi)
        .when()
        .post("/get-random-number")
        .then()
        .statusCode(200)
        .extract()
        .as(GetRandomNumberResponseApi.class);

// 直接执行断言
Assertions.assertEquals(responseApi, actualResponse);

2. (可选)添加Jackson Mutiny扩展(仅特殊场景需要)

如果在其他场景中确实需要序列化/反序列化Uni类型,可以添加Quarkus的Jackson Mutiny扩展到依赖管理文件:

Maven(pom.xml):

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-jackson-mutiny</artifactId>
</dependency>

Gradle(build.gradle):

implementation 'io.quarkus:quarkus-jackson-mutiny'

注意:本测试场景下不需要此扩展,框架已自动处理响应式类型的转换。

内容的提问来源于stack exchange,提问作者Ahmed M. Elzubair

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 07:08:23