Micronaut HTTP Client – Deserialize generic type – For API Testing

Tags: , , , ,



For API testing I have to parse the response of a request using io.micronaut.http.client.HttpClient

I prefer to use the format given below.

Response<User> response =
                client.toBlocking().retrieve(HttpRequest.POST("/", user), Response.class);

But It is not working. I am getting type cast error while trying to fetch User from response

User user = response.getUser();

Currently I am using ObjectMapper (Jackson) like given below,

String response = client.toBlocking().retrieve(HttpRequest.POST("/", user), String.class);
ObjectMapper objectMapper = new ObjectMapper();
Response<User> response = objectMapper.readValue(response, new TypeReference<Response<User>>() {});

Is there any way to use TypeReference directly with HttpClient retrieve method? Or any other way to use generic class type with HttpClient.

Other useful informations

// Response generic class
class Response<T> {
T data;
....
}

Sample response

{
    "data": {
        "name":"sample"
    },
    "message": "Success"
}

Answer

You have several options:

If you need only that User object instance you can do it by retrieve() method where the second parameter is the domain class (User in your case) and then you can use the user variable as you need:

User user = client.toBlocking().retrieve(HttpRequest.POST("/", user), User.class);

assertThat(user)
    .isNotNull()
    .extracting(User::getName)
    .isEqualTo("sample");

If you need also response object (for example to be able to check response code) then you should use exchange() method and then get the User instance by calling body() method on that response:

HttpResponse<User> response = client.toBlocking()
    .exchange(HttpRequest.POST("/", user), User.class);

assertThat(response)
    .isNotNull()
    .extracting(HttpResponse::body)
    .extracting(User::getName)
    .isEqualTo("sample");

Update: When you need to use generic class as a response then you have to use Argument.of() as a second parameter of the retrieve() and exchange() methods where you can specify response generic class and its type parameters. See the example:

Response<User> response = client.toBlocking()
    .retrieve(HttpRequest.POST("/", user), Argument.of(Response.class, User.class));

assertThat(response)
    .isNotNull()
    .extracting(Response::getData)
    .extracting(User::getName)
    .isEqualTo("sample");

If you do it without Argument.of() then it will convert data property into LinkedHashMap instead of User and this:

Response<User> response = client.toBlocking().retrieve(HttpRequest.POST("/", user), Response.class);
response.getData().getName();

… will throw ClassCastException:

java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class my.app.domain.User (java.util.LinkedHashMap is in module java.base of loader ‘bootstrap’; my.app.domain.User.User is in unnamed module of loader ‘app’)



Source: stackoverflow