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).
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);
}
.
.
.
}