Search code examples
junitmockitospring-webclient

Stubbing error while mocking WebClient using Mockito


Based on answers to previous questions like these, I am trying to mock WebClient in a unit test. Please note that I don't wish to use WireMock or MockWebserver.

source method:

public class CoreCustAdapter {
    private final String coreCustBaseUrl;
    private final WebClient webClient;
...
    public PrivateCustomer getPrivateCustomerBasicInformation(String bankId, String 
           customerId) {

            return webClient.get().uri(coreCustBaseUr, bankId)
                    .headers(httpHeaders -> 
                             httpHeaders.set(APICommons.CUSTOMER_ID_VARIABLE_NAME, customerId))
                    .retrieve()
                    .bodyToMono(PrivateCustomer.class).block();
        }

}

This call is actually working, but I am unable to get a unit test working:

@ExtendWith(MockitoExtension.class)
class CoreCustCustomerBasicInfoAdapterTest {

    @Mock
    private WebClient webClient;

    @Mock
    WebClient.RequestHeadersUriSpec requestHeadersUriSpec;

    @Mock
    WebClient.RequestHeadersSpec requestHeadersSpec;

    @Mock
    WebClient.ResponseSpec responseSpec;

    @BeforeEach
    void setUp() {

    }

    @Test
    void getPrivateCustomerBasicInformation_happyFlow()  {

        Mono<PrivateCustomer>privateCustomerMono = MockData.getPrivateCustomerBasicInfo("src/test/resources/core-cust-private-customer-basicinfo.json");

        requestHeadersUriSpec = mock(WebClient.RequestHeadersUriSpec.class);
        requestHeadersSpec = mock(WebClient.RequestHeadersSpec.class);
        responseSpec = mock(WebClient.ResponseSpec.class);

        when(webClient.get()).thenReturn(requestHeadersUriSpec);

        when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
        when(requestHeadersSpec.headers(getHttpHeaders())).thenReturn(requestHeadersSpec);

        when(requestHeadersUriSpec.uri(MockData.CORE_CUST_BASE_URL + APIEndpoints.CORE_CUST_PRIVATE_CUSTOMER_BASIC_INFO, MockData.BANK_ID))
                .thenReturn(requestHeadersSpec);

        when(responseSpec.bodyToMono(PrivateCustomer.class)).
                thenReturn(privateCustomerMono);

        when(spacetimeHttpReactive.createWebClient(externalSystem)).thenReturn(webClient);

        CoreCustCustomerBasicInfoAdapter coreCustCustomerBasicInfoAdapter =
                new CoreCustCustomerBasicInfoAdapter(MockData.CORE_CUST_BASE_URL, spacetimeHttpReactive);

        PrivateCustomer privateCustomer = coreCustCustomerBasicInfoAdapter.getPrivateCustomerBasicInformation(MockData.BANK_ID, MockData.CUSTOMER_ID);

        Assertions.assertNotNull(privateCustomer);
        Assertions.assertEquals(MockData.CUSTOMER_UID, privateCustomer.customerUid());
    }

    private Consumer<HttpHeaders> getHttpHeaders() {
        return httpHeaders -> httpHeaders.set(APICommons.CUSTOMER_ID_VARIABLE_NAME, MockData.CUSTOMER_ID);
    }

}

The partial exception stacktrace:

CustomerInformationUnavailableException(errorMessage=Customers basic information could not be retrieved due to {0}.
, errorCause=org.mockito.exceptions.misusing.PotentialStubbingProblem: 
Strict stubbing argument mismatch. Please check:
 - this invocation of 'headers' method:
    requestHeadersSpec.headers(
    com.core.customer.concept.mgmt.adapter.outbound.core_cust.CoreCustCustomerBasicInfoAdapter$$Lambda/0x0000000800498848@394b9e22
);
    -> at com.core.customer.concept.mgmt.adapter.outbound.core_cust.CoreCustCustomerBasicInfoAdapter.getPrivateCustomerBasicInformation(CoreCustCustomerBasicInfoAdapter.java:45)
 - has following stubbing(s) with different arguments:
    1. requestHeadersSpec.headers(
    com.core.customer.concept.mgmt.adapter.outbound.core_cust.CoreCustCustomerBasicInfoAdapterTest$$Lambda/0x0000000800495b40@6ac83e67
);
      -> at com.core.customer.concept.mgmt.adapter.outbound.core_cust.CoreCustCustomerBasicInfoAdapterTest.getPrivateCustomerBasicInformation_happyFlow(CoreCustCustomerBasicInfoAdapterTest.java:56)
Typically, stubbing argument mismatch indicates user mistake when writing tests.
Mockito fails early so that you can debug potential problem easily.
However, there are legit scenarios when this exception generates false negative signal:
  - stubbing the same method multiple times using 'given().will()' or 'when().then()' API
    Please use 'will().given()' or 'doReturn().when()' API for stubbing.
  - stubbed method is intentionally invoked with different arguments by code under test
    Please use default or 'silent' JUnit Rule (equivalent of Strictness.LENIENT).


Solution

  • I got it working after modifying the headers mock:

    when(requestHeadersSpec.headers(any())).thenReturn(requestHeadersSpec);
    

    Pasting all the mocks(one still needs to refer the original post if more code snippets are required for reference), just in case anyone lands in similar situation:

        @TestInstance(TestInstance.Lifecycle.PER_CLASS)
        @ExtendWith(MockitoExtension.class)
        class HttpRestWebClientTest {
    
        private WebClient webClient;
        private HttpRestWebClient httpRestWebClient;
        WebClient.RequestHeadersUriSpec requestHeadersUriSpec;
        WebClient.RequestHeadersSpec requestHeadersSpec;
        WebClient.ResponseSpec responseSpec;
    
        @BeforeEach
        void setUp() {
    
            webClient = mock(WebClient.class);
    
            requestHeadersUriSpec = mock(WebClient.RequestHeadersUriSpec.class);
            requestHeadersSpec = mock(WebClient.RequestHeadersSpec.class);
            responseSpec = mock(WebClient.ResponseSpec.class);
            
           when(requestHeadersSpec.headers(any())).thenReturn(requestHeadersSpec);
    
           when(webClient.get()).thenReturn(requestHeadersUriSpec);
     when(requestHeadersUriSpec.uri(/*a method returning a test/mock URI*/).thenReturn(requestHeadersSpec);
    
            when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
          }
          .
          .
          .
        }