Mock Service inside resource using jersey test framwork
Asked Answered
A

1

7

I have a resource for rest API which uses a service. This service has a constructor with parameters. I want to test this resource and to mock this service. This Question: How to pass parameters to REST resource using Jersey 2.5

wasn't helpful because they used @Inject and I cannot use it. Any suggestions?

The second question is how do I pass parameter to test this resouce: My code is:

@Path("/2/{subversion: [0-3]}/users")
public class UserResource {
    Logger log = Logger.getLogger(UserResource.class);
    private MyService service;
    public void setService(Service ser) {
        this.service = ser;
    }

@Context HttpServletRequest currentRequest;

@GET
@Produces("application/json")
public Response getUsers(@Context HttpHeaders httpHeaders, @Context UriInfo
uriInfo) {
// my function
}
}

How can I pass "httpHeaders" and "UriInfo". My test looks like this:

Response response = target("/2/0/users/").request().get();
Users users = response.readEntity(Users.class);
assertNotNull(users);
Astonishment answered 19/1, 2016 at 22:57 Comment(0)
T
9

For the service, it's good practice to either inject through the constructor or setter. This makes it easy to mock and pass in during unit testing. As for the mocking, you should use a framework like Mockito. Then you can do stuff like

MyService service = Mockito.mock(MyService.class);
when(service.getObject()).thenReturn(new Object());

HttpHeaders headers = Mockito.mock(HttpHeaders.class);
when(headers.getHeaderString("X-Header")).thenReturn("blah");

UriInfo uriInfo = Mockito.mock(UriInfo.class);
when(uriInfo.getRequestUri()).thenReturn(URI.create("http://localhost"));

Then you can just pass all these mocks to your resource class when UNIT testing.

For INTEGRATION testing you won't need to mock the headers or uriinfo. The actual ones will get passed in. But you can still mock the service if you want. Here's an example

public class MockServiceTest extends JerseyTest  {

    public static interface Service {
        String getMessage(String name);
    }

    @Path("message")
    public static class MessageResource {

        private final Service service;

        public MessageResource(Service service) {
            this.service = service;
        }

        @GET
        public String get(@QueryParam("name") String name,
                          @Context HttpHeaders headers,
                          @Context UriInfo uriInfo) {
            String nameQuery = uriInfo.getQueryParameters().getFirst("name");
            String header = headers.getHeaderString("X-Header");
            assertNotNull(nameQuery);
            assertNotNull(header);
            return service.getMessage(name);
        }
    }

    private Service service;

    @Override
    public ResourceConfig configure() {
        service = Mockito.mock(Service.class);
        return new ResourceConfig().register(new MessageResource(service));
    }

    @Test
    public void testIt() {
        Mockito.when(service.getMessage("peeskillet")).thenReturn("Hello peeskillet");

        Response response = target("message").queryParam("name", "peeskillet").request()
                .header("X-Header", "blah")
                .get();
        assertEquals(200, response.getStatus());
        assertEquals("Hello peeskillet", response.readEntity(String.class));
    }
}
Thousandth answered 20/1, 2016 at 1:5 Comment(9)
Thank you for your response. One more question regarding that is what can I do If I have in my resource property with @context attribute? I updated my post with thatAstonishment
If you're running a Unit test, mock it like I described above. If you are running an integration test with the test framework, you need to configure it to run in a servlet container, in order for the HttpServletRequest to be injected. See hereThousandth
Looks like the binding is not working for me. I created a Binder for HttpServletRequest and putted some breakpoints, and nothing happen. I am doing integration test.Astonishment
You don't need a binder for the HttpServletRequest. It's created for you. That example only uses a binder because it is trying to inject an HttpSession. The main point you need to consider about the example is the GrizzlyWebTestContainerFactory and the DeploymentContextThousandth
How is MessageResource created in non-test mode? You are using constructor with single argument - in my case, jersey complaints that it could not find suitable constructorCephalochordate
@WandMaker You can register the resource as an instance. I guess I hadn't considered the non-test mode. But what you should do if you don't want to register the resource as an instance, the use dependency injection. See stackoverflow.com/documentation/jersey/7016/…. The examples don't show it, but you can use constructor injection. Put the @Inject on the constructor, instead of fiieldsThousandth
@WandMaker Here is an example how to use @Inject on the constructor https://mcmap.net/q/330002/-jersey-how-to-mock-serviceLawerencelawes
@PaulSamsotha Since the documentation is shut down I posted the link of your answer with a clear example using @InjectLawerencelawes
@PaulSamsotha I am not quite sure it's correct. Firstly, your class is a static class within the same Test class. this is not a practical scenario. You have to have a Resource Controller/POJO outside test class and you would mock that inside using mock().Silesia

© 2022 - 2024 — McMap. All rights reserved.