Ahmed M. Elzubair
Ahmed M. Elzubair

Reputation: 107

How to test reactive APIs in Quarkus using RestAssured

I have Quarkus app and I am trying to test my reactive Controller REST APIs using RestAssured.

And I am using smallrye-mutiny for developing my REST APIs.

The issue is when running the test RestAssured is trying to serialize the Uni and it throwing the following exception saying it could not found a serializer for Uni type. :

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]

Here is how my code looks like:

My Service Layer:

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;
}

My Controller :

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);
}

And finally here is my test how it looks like.

@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);
}

Any help, advices, resources?

Upvotes: 1

Views: 693

Answers (2)

Ahmed M. Elzubair
Ahmed M. Elzubair

Reputation: 107

After 18 hours of searching I came to know that I need to add quarkus-resteasy-mutiny to my pom.xml

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

I think this dependency contains the Uni serializer by default.

Upvotes: 0

zforgo
zforgo

Reputation: 3316

That's because RestAssured tries to serialize a mock object. That mock object has extra properties like mockitoInterceptor and so on. And one or more of those properties in the tree are not serializable by Jackson.

So use real objects as request body and as expected response.

The following test will work

@QuarkusTest
class ExampleResourceTest {

    @InjectMock
    RandomNumberService service;

    @Test
    void testRandomNumberEndpoint() {

        // create a real (not mocked) request object
        var request = new RandomNumberRequestDTO();
        request.setUpper(500L);
        request.setLower(3L);

        // create a real (not mocked) response object
        var expectedResponse = new RandomNumberResponseDTO(3L, 500L, 399L);

        // mock service with real request and response object
        Mockito.when(service.getRandomNumber(request)) //pass real request object
                .thenReturn(Uni.createFrom().item(expectedResponse)); // pass real response object

        var resp =
                given()
                    .when()
                        .body(request) // pass real request object
                        .contentType(ContentType.JSON)
                        .post("/random-number")
                    .then()
                        .statusCode(200)
                        .extract()
                        .as(RandomNumberResponseDTO.class);
        assertAll(
                () -> assertNotNull(resp),
                () -> assertEquals(request.getLower(), resp.getLower()),
                () -> assertEquals(request.getUpper(), resp.getUpper()),
                () -> assertThat(resp.getRandomNumber(), is(both(greaterThanOrEqualTo(resp.getLower())).and(lessThan(resp.getUpper()))))
        );
    }

}

Upvotes: 0

Related Questions