Mocking WebClient post method is failing

Viewed 299

I could find several questions regarding mocking a WebClient object. But I still have problems when doing a post with a body and having multiple header values. I'm just using Mockito.

public Boolean addNote(AlarmModel model) {
        ServiceDTO dto = mapper(model);

        return webClient.post()
                .uri("/service/api/addNotes")
                .headers(getHttpHeaders(dto.getHeader()))
                .accept(MediaType.APPLICATION_JSON)
                .body(Mono.just(dto.getBody()), ServiceBodyDTO.class)
                .retrieve()
                .onStatus(HttpStatus::is5xxServerError, this::handleStatusCodeError)
                .onStatus(HttpStatus::is4xxClientError, this::handleStatusCodeError)
                .bodyToMono(Boolean.class)
                .block();
    }

And this is how I am mocking the behavior of the post method.

        when(webClientMock.post()).thenReturn(requestBodyUriMock);
        when(requestBodyUriMock.uri(anyString())).thenReturn(requestBodyMock);
        
        when(requestHeadersMock.headers(any())).thenReturn(requestHeadersMock);

        when(requestBodyMock.accept(any())).thenReturn(requestBodyMock);
        when(requestBodyMock.contentType(any())).thenReturn(requestBodyMock);
        when(requestBodyMock.bodyValue(any())).thenReturn(requestHeadersMock);
        when(requestHeadersMock.retrieve()).thenReturn(responseMock);
        when(responseMock.bodyToMono(Boolean.class))
                .thenReturn(Mono.just(true));

But, when I execute this test case, it fails at the line having retrieve() And the exception is java.lang.NullPointerException

Did I miss anything here? TIA.

2 Answers

Agree with @Martin's comment that unit testing webclient has very low ROI.

I would recommend WireMock which provides very good API for testing web clients. Here are some examples

stubFor(post("/service/api/addNotes")
        .withHeader(HttpHeaders.ACCEPT, containing(MediaType.APPLICATION_JSON_VALUE))
        .willReturn(aResponse()
                .withStatus(200)
                .withBody("true")
        )
);

StepVerifier.create(service.addNote(note))
        .expectNextCount(1)
        .verifyComplete();

You could easily test both positive and negative scenarios by providing different stubs

stubFor(post("/service/api/addNotes")
        .withHeader(HttpHeaders.ACCEPT, containing(MediaType.APPLICATION_JSON_VALUE))
        .willReturn(aResponse()
                .withStatus(500)
        )
);

test retry logic using Scenarios or even simulate timeouts using Delays

ps

not sure why you are using block but my example is using StepVerifier and assumes that addNote returns Mono<Boolean>

This is what works for me, not too different from yours. You might want to compare and feedback :)

@Mock
private WebClient webClient;

@Mock
WebClient.RequestBodyUriSpec requestBodyUriSpec;

@SuppressWarnings("rawtypes")
@Mock
WebClient.RequestHeadersSpec requestHeadersSpec;

@Mock
WebClient.RequestBodySpec requestBodySpec;

@Mock
WebClient.ResponseSpec responseSpec;

given(getWebClient()).willReturn(webClient);
given(webClient.post()).willReturn(requestBodyUriSpec);
given(requestBodyUriSpec.uri(anyString())).willReturn(requestBodySpec);
given(requestBodySpec.headers(any())).willReturn(requestBodySpec);      
given(requestBodySpec.contentType(any())).willReturn(requestBodySpec);
given(requestBodySpec.accept(any())).willReturn(requestBodySpec);
given(requestBodySpec.bodyValue(any())).willReturn(requestHeadersSpec);
given(requestHeadersSpec.retrieve()).willReturn(responseSpec);
Related