javagsonjersey-2.0jersey-test-framework

MessageBodyProviderNotFoundException is thrown in JerseyTest when using GSON


I use Jersey and decided to go with GSON instead of Moxy for JSON handling (didn't like the fact that Moxy requires setters).

Everything works fine until now, except one very annoying issue in my JerseyTest subclasses: custom GsonProvider is not being recognized unless explicitly registered for each call. It is, however, being recognized if I deploy the application to Tomcat.

My ResourceConfig:

@ApplicationPath("")
public class MyResourceConfig extends ResourceConfig {

    public MyResourceConfig() {
        register(GsonProvider.class);

        register(SomeResource.class);
    }
}

Implementation of GsonProvider (though I don't think it is related to the issue I experience):

@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class GsonProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {

    private final Gson mGson;

    public GsonProvider() {
        mGson = new GsonBuilder().create();
    }

    @Override
    public boolean isReadable(Class<?> type, Type genericType,
                              Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public T readFrom(Class<T> type, Type genericType, Annotation[] annotations,
                      MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
                      InputStream entityStream) throws IOException, WebApplicationException {

        InputStreamReader reader = new InputStreamReader(entityStream, "UTF-8");
        try {
            return mGson.fromJson(reader, type);
        } finally {
            reader.close();
        }
    }

    @Override
    public boolean isWriteable(Class<?> type, Type genericType,
                               Annotation[] annotations, MediaType mediaType) {
        return true;
    }

    @Override
    public long getSize(T t, Class<?> type, Type genericType,
                        Annotation[] annotations, MediaType mediaType) {
        return -1;
    }

    @Override
    public void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations,
                        MediaType mediaType, MultivaluedMap<String, Object> httpHeaders,
                        OutputStream entityStream) throws IOException, WebApplicationException {

        PrintWriter printWriter = new PrintWriter(entityStream);
        try {
            String json = mGson.toJson(t);
            printWriter.write(json);
            printWriter.flush();
        } finally {
            printWriter.close();
        }
    }
}

This test results in MessageBodyProviderNotFoundException:

public class SomeResourceTest extends JerseyTest {
    @Override
    public Application configure() {
        return new MyResourceConfig();
    }

    @Test
    public void someApi_200Returned() throws Exception {
        // Arrange
        // Act
        SomeResponse response =
                target("/somepath")
                        .request()
                        .post(Entity.json(""), SomeResponse.class);
        // Assert
        assertThat(response.getStatus(), is(200));
    }
}

In order to resolve this issue I register GsonProvider for request. The following change makes the test pass:

public class SomeResourceTest extends JerseyTest {
    @Override
    public Application configure() {
        return new MyResourceConfig();
    }

    @Test
    public void someApi_200Returned() throws Exception {
        // Arrange
        // Act
        SomeResponse response =
                target("/somepath")
                        .register(GsonProvider.class)
                        .request()
                        .post(Entity.json(""), SomeResponse.class);
        // Assert
        assertThat(response.getStatus(), is(200));
    }
}

So, registration of GsonProvider in MyResourceConfig is good for deployment, but JerseyTest requires additional registration per request.

While I can live with that, it is annoying, time consuming and will be hard to communicate to other team members. Any solution for this issue?


Solution

  • You haven't shown the stacktrace, but I'm pretty sure that if you look closely at it, it will show that it is actually a client side error. What you need to do is register the gson provider with the client also since you are trying to deserialize the response JSON to a POJO

    @Override
    public void configureClient(ClientConfig config) {
        config.register(GsonProvider.class)
    }
    

    The configureClient method is a method in the JerseyTest that you can override.