Guru
Guru

Reputation: 302

Unable to suspend a connection of an asynchronous request while executing an unit test for a JAX-RS endpoint using DropWizard

Am trying to write a unit test for a JAX-RS resource deployed in Dropwizard but I am getting a javax.ws.rs.ProcessingException.

I have written down the code that I have attempted.

The below interface is placed within the project api folder structure.

@Path("/doPostRequest")
public interface SomeResource {

    @POST 
    @Consumes({ "application/json" })
    @Produces({ "application/json" })
    void checkAndDoPostRequest(
        @Suspended SomeResponse response,
        @Valid SomeDetailsClass  someDetailsClassObject;
    );
}

I have a class implementing this interface in the service folder of the project structure as shown below.

public class SomeResourceImpl implements SomeResource {

    private SomeProcessor someProcessor;

    @Inject
    public SomeResourceImpl(final 
    SomeProcessor someProcessor) {
        this.someProcessor = someProcessor;
    }

    @Override
    @RolesAllowed("read")
    public void checkAndDoPostRequest(
        SomeResponse response,
        SomeDetailsClass  someDetailsClassObject) {       

      response.resume(someProcessor.doProcess(someDetailsClassObject));
    }
}

I have previously written Unit tests for endpoints by extending the JerseyTest class and using the target method to send a request to this endpoint. But in the case of dropwizard I am not entirely sure.

Moreover, this endpoint requires two objects, i.e., in the above interface the "checkAndDoPostRequest" method requires two objects and I am not sure how I can send both the objects using the "target" method in JerseyTest.

Here is the Unit test that I have attempted.

public class SomeResourceImplTest {

    private ServiceLocator serviceLocator;

    private static String final URL = "/doPostRequest";

    @Rule
    public ResourceTestRule resources = ResourceTestRule.builder()
        .addResource(new SomeResourceImpl())
        .build();

    @Test
    public void testPostRequest() {
        Client client = resources.client();
        resources.getJerseyTest().target(URL).request()
            .post(Entity.entity(buildDetailsObject(), 
            MediaType.APPLICATION_JSON_TYPE));
    }

    private SomeDetailsClass  buildDetailsObject() {
       //code to build this object;
    }

}

When I try to run the above test case, I get the following exception

javax.ws.rs.ProcessingException: Server-side request processing failed with an error.
at org.glassfish.jersey.test.inmemory.InMemoryConnector$InMemoryResponseWriter.failure(InMemoryConnector.java:167)
at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:509)
at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:334)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
at org.glassfish.jersey.test.inmemory.InMemoryConnector.apply(InMemoryConnector.java:275)
at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:252)
at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:684)
at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:681)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:228)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:681)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:437)
at org.glassfish.jersey.client.JerseyInvocation$Builder.post(JerseyInvocation.java:343)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at io.dropwizard.testing.junit.ResourceTestRule$1.evaluate(ResourceTestRule.java:174)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:68)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:74)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:161)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: javax.ws.rs.ProcessingException: Attempt to suspend a connection of an asynchronous request failed in the underlying container.
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:328)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)
... 49 more

I tried appending the "async()" function in the unit test identical to below

public void testPostRequest() {
        Client client = resources.client();
        resources.getJerseyTest().target(URL).request().async()
            .post(Entity.entity(buildDetailsObject(), 
            MediaType.APPLICATION_JSON_TYPE));
    }

But the above implementation simply prevents the processing exception but the doesn't run the test correctly i.e., the request doesn't hit the endpoint.

Can someone please help.

Upvotes: 2

Views: 494

Answers (1)

Pedram Esmaeeli
Pedram Esmaeeli

Reputation: 174

I faced the same problem. There is a solution for it in this Github issue.

Looks like InMemoryConnector doesn't support endpoints that can be suspended and are async, so a new testing container is needed. You may need to try out the Grizzly test web server. I was able to swap it out and have my test cases pass.

You should use the following line when building the resource rule and of course add the Grizzly dependency.

setTestContainerFactory(new GrizzlyWebTestContainerFactory())

There is code sample and more information in the issue.

Upvotes: 2

Related Questions