使用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
相关产品推荐
相关产品推荐

