Vanshaj Behl
Vanshaj Behl

Reputation: 143

ResourceInfo @Mock not working, throwing NPE in QuarkusTest

I am trying to create unit test for my interceptor class ApiMetrics

@Provider
public class ApiMetrics implements ContainerRequestFilter, ContainerResponseFilter {

@Context
ResourceInfo resourceInfo;

@Context
HttpServerRequest httpServerRequest;
....
}

My Test Class is as follows, but on using @Mock annotation i'm getting NPE on resourceInfo and httpServerRequest when trying to mock any of their methods (getResourceMethod). Also, I tried @Inject, @InjectMock as I'm not sure how to mock a @context field but got dependency error.

@QuarkusTest
class CLASS_ApiMetrics_UT_Test {

@Inject
ApiMetrics apiMetrics;

@Mock
ResourceInfo resourceInfo;

@Mock
HttpServerRequest httpServerRequest;
....
}

Also if I use the following for mocking,

ResourceInfo resourceInfo = Mockito.mock(ResourceInfo.class);
HttpServerRequest httpServerRequest = Mockito.mock(HttpServerRequest.class);
when(resourceInfo.getResourceMethod()).thenReturn(method);
when(httpServerRequest.getFormAttribute(BODY_REQUESTER_ID)).thenReturn(expectedRequesterId);

I get this error for the mocked Classes Method threw 'org.jboss.resteasy.spi.LoggableFailure' exception. Cannot evaluate com.sun.proxy.$Proxy141.toString()

enter image description here

Upvotes: 1

Views: 426

Answers (2)

Holly Cummins
Holly Cummins

Reputation: 11502

The resourceInfo and httpServerRequest fields should be annotated with @InjectMock, not @Mock.

The @Mock annotation doesn't actually mark an injection point, which is why nothing is getting injected and you're seeing the NPE. io.quarkus.test.Mock is intended to be applied to an actual mock implementation, not the injection point. It defines a global mock which would be used everywhere something used @Inject, in test mode.

Test with @InjectMock

Here's what your test should look like

@QuarkusTest
class CLASS_ApiMetrics_UT_Test {

    @Inject
    ApiMetrics apiMetrics;

    @InjectMock
    ResourceInfo resourceInfo;

    @InjectMock
    HttpServerRequest httpServerRequest;

    @BeforeEach
    public void setup() {
       when(resourceInfo.getResourceMethod()).thenReturn(method);       
       when(httpServerRequest.getFormAttribute(BODY_REQUESTER_ID)).thenReturn(expectedRequesterId);
    }

Using @Mock

If you wanted to use the global @Mock approach instead, you would do something like

@Inject
ResourceInfo resourceInfo;

in the test class (note that it's just a normal @Inject). Then you'd manually define a mock implementation class:

@Mock
public class MockResourceInfo implements ResourceInfo {

    public void doAThing() {
       // mock implementation goes here
    }

    public void notifyInvoiceAlreadySent(Invoice invoice) {

    }
}

That way of doing it is sometimes handy, but it has the disadvantage that Mockito can't help you define the mock, and the same mock implementations is used in all test classes. So generally @InjectMock is better.

The Quarkus docs have more explanation of the different mock annotations.

Upvotes: 0

Luca Basso Ricci
Luca Basso Ricci

Reputation: 18413

You can write a resource visible only for your test(s); no need to mock @Context objects because during test all is working as in a real http call PS: I suspect this type of objects can't be mocked,but someone from Quarkus team can give you a precise answer about that.

@QuarkusTest
@TestHttpEndpoint(TestResource.class)
class CLASS_ApiMetrics_UT_Test {
  @Inject
  ApiMetrics apiMetrics;

  @Test
  void test() {
    RestAssured.given().get().then().statusCode(200);
    assertCondition(expected, apiMetrics.propertyToTest);
  }

  @Path("api-metrics-test")
  public static class TestResource {
    @Path("")
    @GET
    public void doSomething() {...}
  }
}

Upvotes: 0

Related Questions