My server sends a request via WebClient and the code is below:
public String getResponse(String requestBody){
...
WebClient.RequestHeadersSpec<?> request =
client.post().body(BodyInserters.fromValue(requestBody));
String resp =
request.retrieve().bodyToMono(String.class)
.doOnError(
WebClientResponseException.class,
err -> {
// do something
})
.block();
return resp;
}
I wrote a unit test for it and want to mock the WebClient so that I can receive the expected response:
when(webClientMock.post()).thenReturn(requestBodyUriMock);
when(requestBodyUriMock.body(BodyInserters.fromValue(requestBody))).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(String.class)).thenReturn(Mono.just("response"));
String response = someServiceSpy.getResponse(requestBody);
assertEquals(Mono.just("response"), response);
However, the result is not the "response"
but a html file. I think I made a mistake somewhere but I don't know how to fix it.
CodePudding user response:
It seems the client
referenced in your getResponse
method is not set to the mock you have created (webClientMock
) in your test.
If you are creating this client
object in your getResponse
method, I would suggest that you create it using a method that you could mock. Something like
WebClient buildWebClient() {
// build your webclient using the WebClientBuilder
}
You may want to throw a comment and or a @VisibleForTesting
annotation on there so it is clear this method exists in order to make testing easier.
Then you can stub this method in your someServiceSpy
:
Mockito.doReturn(mockWebClient).when(someServiceSpy).buildWebClient();
This will ensure that your mockWebClient
is used in your getResponse
method in your test.
Additionally, it seems as though your existing code needs a slight edit.
when(requestBodyUriMock.body(BodyInserters.fromValue(requestBody))).thenReturn(requestHeadersMock);
Should be
when(requestBodyUriMock.body(eq(BodyInserters.fromValue(requestBody)))).thenReturn(requestHeadersMock);
CodePudding user response:
I have figured out the solution that mocks the WebClient
directly instead of putting the build logic into a new method to mock it. I wrote my solution here in case someone else needs it future:
Let me put the code example here:
final WebClient client =
WebClient.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(someValue))
.clientConnector(new ReactorClientHttpConnector(HttpClient.create(someProvider)))
.baseUrl(someUrl)
.defaultHeader(contentType, TEXT_XML_VALUE)
.build();
final WebClient.RequestHeadersSpec<?> request =
client.post().body(BodyInserters.fromValue(reqBody));
First, we must mock the static method builder()
of WebClient
. If this method is not mocked, mockito can't edit the behavior of this method, and the mocked WebClient
would not be used. I found this from the answer to this StackOverflow question; you can read it for more details.: How to mock Spring WebClient and builder
After mocked the builder()
with the method provided by the above anwser, you will get a mocked WebClient
, it's something like:
when(webClientBuilder.build()).thenReturn(webClientMock);
Then you can start to finish the rest of the work. In my sample code, the client
will invoke post()
and body()
, so write the following:
when(requestBodyUriMock.body(any())).thenReturn(requestHeadersMock);
when(requestHeadersMock.retrieve()).thenReturn(responseMock);
when(responseMock.bodyToMono(String.class)).thenReturn(Mono.just(expectedResponse));
My unit test returned the NPE at the beginning and it because I used
when(requestBodyUriMock.body(BodyInserters.fromValue(requestBody))).thenReturn(requestHeadersMock);
instead of
when(requestBodyUriMock.body(any())).thenReturn(requestHeadersMock);
I think it is because the code not "think" the requestBodyUriMock
is using the BodyInserters.fromValue(requestBody
for some reasons that I haven't know yet. After I changed it to any()
, it worked.