Home > Enterprise >  Why this java WebClient mock is not working
Why this java WebClient mock is not working

Time:11-04

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.

  • Related