How to mock generic wildcard Class?

127 Views Asked by At

I have a variable which is generic i.e

ParameterizedTypeReference<?> parameterizedTypeReference = new ParameterizedTypeReference<List<Object>>() {}

The actual method looks like:

public <T, U> U post(String serviceUrl, T data, ParameterizedTypeReference<U> ref, String bearerToken) {
        ParameterizedTypeReference<?> parameterizedTypeReference = new ParameterizedTypeReference<List<Object>>() {
        }:
        var responseBodyMono = webClient
                .post()
                .uri(serviceUrl)
                .header(HttpHeaders.AUTHORIZATION, bearerToken)
                .header(X_CORRELATION_ID, MDC.get(X_CORRELATION_ID))
                .body(Mono.just(data), parameterizedTypeReference)              // I want to mock this
    }

I have written test case like

@Mock
WebClient.RequestHeadersSpec requestHeadersSpec;

@Mock
WebClient.RequestHeadersUriSpec requestHeadersUriSpec;

@Mock
WebClient.RequestBodyUriSpec requestBodyUriSpec;

@Test
    public void testPost() {
        ParameterizedTypeReference<?> parameterizedTypeReference1 = new ParameterizedTypeReference<List<Object>>() {};
        Mockito.when(webClient.post()).thenReturn(requestBodyUriSpec);
        when(requestBodyUriSpec.uri(SERVICE_URL)).thenReturn(requestBodySpec);
        when(requestBodySpec.header(eq(HttpHeaders.AUTHORIZATION), eq(BEARER_TOKEN))).thenReturn(requestBodyUriSpec);
        when(requestBodyUriSpec.header(anyString(), Mockito.isNull())).thenReturn(requestBodyUriSpec);
        when(requestBodyUriSpec.body(Mono.just(anyString()), eq(parameterizedTypeReference1))).thenReturn(requestHeadersSpec); // Here I have tried to mock
        assertNotNull(webClientService.post(SERVICE_URL, Collections.emptyList(), parameterizedTypeReference, BEARER_TOKEN));
    }

But I get error saying

org.mockito.exceptions.misusing.PotentialStubbingProblem: 
Strict stubbing argument mismatch. Please check:
 - this invocation of 'body' method:
    requestBodyUriSpec.body(
    MonoJust,
    ParameterizedTypeReference<T>
);
    -> at com.radisys.macs.cems.common.webclient.WebClientService.getResponseEntity(WebClientService.java:310)
 - has following stubbing(s) with different arguments:
    1. requestBodyUriSpec.body(MonoJust, null);
      -> at com.radisys.macs.cems.common.webclient.WebClientServiceTest.testGetResponseEntity(WebClientServiceTest.java:224)

How can the parametrizedTypeReference be null? When I have nicely mocked it?

EDIT After trying the answer, the following was the error

java: no suitable method found for thenReturn(reactor.core.publisher.Mono<java.util.List<java.lang.String>>)
    method org.mockito.stubbing.OngoingStubbing.thenReturn(reactor.core.publisher.Mono<capture#1 of ?>) is not applicable
      (argument mismatch; inference variable T has incompatible bounds
          equality constraints: capture#1 of ?
          lower bounds: java.util.List<T>)
    method org.mockito.stubbing.OngoingStubbing.thenReturn(reactor.core.publisher.Mono<capture#1 of ?>,reactor.core.publisher.Mono<capture#1 of ?>...) is not applicable
      (argument mismatch; inference variable T has incompatible bounds
          equality constraints: capture#1 of ?
          lower bounds: java.util.List<T>)

EDIT 2

Changed to

 ParameterizedTypeReference parameterizedTypeReference1 = new ParameterizedTypeReference<List<Object>>() {}; // removed <?>
org.mockito.exceptions.misusing.PotentialStubbingProblem: 
Strict stubbing argument mismatch. Please check:
 - this invocation of 'body' method:
    requestBodyUriSpec.body(
    MonoJust,
    ParameterizedTypeReference<java.util.List<java.lang.Object>>
);
    -> at com.radisys.macs.cems.common.webclient.WebClientService.post(WebClientService.java:185)
 - has following stubbing(s) with different arguments:
    1. requestBodyUriSpec.body(null, null);
      -> at com.radisys.macs.cems.common.webclient.WebClientServiceTest.testPost(WebClientServiceTest.java:131)

Extra info please ignore this, for the SO


If this helps EXTRA INFO:

For the below code

 public <T, U> U update(String serviceUrl, T payload, ParameterizedTypeReference<U> ref, String bearerToken) {
        LOGGER.atFine().log(SERVICE_URL_LOGGER, serviceUrl);
        ParameterizedTypeReference<T> parameterizedTypeReference = new ParameterizedTypeReference<>() {
        };
        Mono<U> responseStatusMono = webClient
                .put()
                .uri(serviceUrl)
                .header(HttpHeaders.AUTHORIZATION, bearerToken)
                .header(X_CORRELATION_ID, MDC.get(X_CORRELATION_ID))
                .body(Mono.just(payload), parameterizedTypeReference)
                .retrieve()
                .onStatus(HttpStatusCode::isError, response -> com.radisys.macs.cems.common.webclient.SBServiceErrorHandlerUtil.logAndGetThrowableMono(serviceUrl,
                        response))
                .bodyToMono(ref)
                .doOnError(com.radisys.macs.cems.common.webclient.SBServiceErrorHandlerUtil::handleConnectionErrors);
        var responseStatus = responseStatusMono.block();
        LOGGER.atFine().log("update operation response from SB Service: %s", responseStatus);
        return responseStatus;
    }

Test case is

    @Test
    public void testUpdate() {

        when(webClient.put()).thenReturn(requestBodyUriSpec);
        when(requestBodyUriSpec.uri(WebClientServiceTest.SERVICE_URL)).thenReturn(requestBodySpec);
        when(requestBodySpec.header(eq(HttpHeaders.AUTHORIZATION), eq(WebClientServiceTest.BEARER_TOKEN))).thenReturn(requestBodyUriSpec);
        when(requestBodyUriSpec.header(anyString(), Mockito.isNull())).thenReturn(requestBodyUriSpec);
        when(requestBodyUriSpec.body(any(), any(ParameterizedTypeReference.class))).thenReturn(requestHeadersSpec);
        when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
        when(responseSpec.onStatus(any(), any())).thenReturn(responseSpec);
        when(responseSpec.bodyToMono(eq(parameterizedTypeReference))).thenReturn(Mono.just(Collections.singletonList("record1")));

        assertNotNull(webClientService.update(WebClientServiceTest.SERVICE_URL, Collections.emptyList(), parameterizedTypeReference, WebClientServiceTest.BEARER_TOKEN));


    }

The below is working just fine, but for the post it is not working, I am amazed how it is working for update, and not for post request :(

1

There are 1 best solutions below

4
On

You cannot mix matcher arguments with non-matcher arguments.

when(requestBodyUriSpec.body(
    Mono.just(anyString()),
    eq(parameterizedTypeReference1))) // eq returns null!
  .thenReturn(requestHeadersSpec);

Mixes a non-matcher argument (Mono.just) with a matcher argument (ArgumentMatchers.eq). The implementation of the matcher methods returns null (or the default value for the type, e.g. false for booleans and 0 for numeric types).

Mockito should usually catch this and would normally print an error message informing about the mixed usage. The problem is that you record a second matcher as argument to Mono.just, preventing Mockito from detecting this mistake.

You need to use matchers for all arguments or no matchers at all. Here's a correct version using matchers for all arguments:

when(requestBodyUriSpec.body(
    any(Mono.class),
    eq(parameterizedTypeReference1)))
  .thenReturn(requestHeadersSpec);