Mocking a WebClient post when there's a request body

12,837

Solution 1

I missed mocking the "header" method of requestHeadersSpec:

when(requestHeadersSpec.header(any(),any())).thenReturn(requestHeadersSpec);

So, this works fine now:

@Test
    public void postTest() throws IOException {
        when(webClient.post()).thenReturn(requestBodyUriSpec);
        when(requestBodyUriSpec.uri(anyString())).thenReturn(requestBodySpec);
        when(requestBodySpec.header(any(),any())).thenReturn(requestBodySpec);

        when(requestHeadersSpec.header(any(),any())).thenReturn(requestHeadersSpec);

        when(requestBodySpec.accept(any())).thenReturn(requestBodySpec);
        when(requestBodySpec.body(any())).thenReturn(requestHeadersSpec);
        when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
        when(responseSpec.bodyToMono(ArgumentMatchers.<Class<String>>notNull()))
                .thenReturn(Mono.just("resp"));

        Assert.assertNotNull(restClient.post("http://sampleurl",Object.class, Object.class));
    }

Solution 2

After adding headers in my WebClient code It starts to work like a magic

    @Test
    public void postMethod() {
        when(webClient.post()).thenReturn(requestBodyUriMock);
        when(requestBodyUriMock.uri(anyString())).thenReturn(requestBodyMock);
        when(requestBodyMock.header(any(),any())).thenReturn(requestBodyMock);

        when(requestHeadersMock.header(any(),any())).thenReturn(requestHeadersMock);

        when(requestBodyMock.accept(any())).thenReturn(requestBodyMock);
        when(requestBodyMock.contentType(any())).thenReturn(requestBodyMock);
        when(requestBodyMock.body(any())).thenReturn(requestHeadersMock);
        when(requestHeadersMock.retrieve()).thenReturn(responseMock);
        when(responseSpec.bodyToMono(String.class))
                .thenReturn(Mono.just("output"));
    
        //WebClient call
        TestResponse test = service.testMethod(mockObject);
        assertEquals(test.Status(), 200);
    }

Solution 3

You do not need to create a new RestClient() if you are using @InjectMocks. Also Since you are mocking WebClient you do not need to mock WebClient.*` .

So the code becomes

@Mock
WebClient webClient;

@InjectMocks
RestClient restClient;

@Test
    public void postTest() throws IOException {
        when(webClient.post()).thenReturn(requestBodyUriSpec);
        when(requestBodyUriSpec.uri(anyString())).thenReturn(requestBodySpec);
        when(requestBodySpec.header(any(),any())).thenReturn(requestBodySpec);
        when(requestBodySpec.accept(any())).thenReturn(requestBodySpec);
        when(responseSpec.bodyToMono(ArgumentMatchers.<Class<String>>notNull()))
                .thenReturn(Mono.just("resp"));

        //when(requestBodySpec.body(any())).thenReturn(requestHeadersSpec);
        when(requestBodySpec.retrieve()).thenReturn(responseSpec);

        restClient.post("http://sampleurl",Object.class, Object.class);
    }
Share:
12,837
AliReza
Author by

AliReza

I like programming, software development and microservice architecture.

Updated on July 09, 2022

Comments

  • AliReza
    AliReza almost 2 years

    There are several questions with helpful answers about mocking a WebClient object. But I still have problems when doing a post with a body. I'm just using Mockito not mockwebserver.

    This is the method I'm testing:

    public class RestClient extends BaseRestClient {
     ...
     public <T,G> Mono<T> post(String url, G req, Class<T> resp) throws IOException {
            Mono<T> response = null;
    
            response = this.getWebClient().post()
                        .uri(url)
                        .header(HttpHeaders.CONTENT_TYPE,JSON_CONTENT_TYPE)
                        .accept(MediaType.APPLICATION_JSON)
                        //.body(BodyInserters.fromObject(req))
                        .header(HttpHeaders.AUTHORIZATION, BEARER + token)
                        .retrieve()
                        .bodyToMono(resp).log();
    
            return response.map(resp::cast);
        }
     ...
    

    Notice the commented out body line.

    And this is the test which works fine with the code above- again notice the commented out line in the test:

    @Mock
    WebClient webClient;
    
    @Mock
    WebClient.RequestBodyUriSpec requestBodyUriSpec;
    
    @Mock
    WebClient.RequestHeadersSpec requestHeadersSpec;
    
    @Mock
    WebClient.RequestBodySpec requestBodySpec;
    
    @Mock
    WebClient.ResponseSpec responseSpec;
    
    @InjectMocks
    RestClient restClient;
    
    @Test
        public void postTest() throws IOException {
            when(webClient.post()).thenReturn(requestBodyUriSpec);
            when(requestBodyUriSpec.uri(anyString())).thenReturn(requestBodySpec);
            when(requestBodySpec.header(any(),any())).thenReturn(requestBodySpec);
            when(requestBodySpec.accept(any())).thenReturn(requestBodySpec);
            when(responseSpec.bodyToMono(ArgumentMatchers.<Class<String>>notNull()))
                    .thenReturn(Mono.just("resp"));
    
            //when(requestBodySpec.body(any())).thenReturn(requestHeadersSpec);
            when(requestBodySpec.retrieve()).thenReturn(responseSpec);
    
            restClient.post("http://sampleurl",Object.class, Object.class);
        }
    

    Again, everything works fine. But if I put the commented out line back in the code, meaning there's a body to this post, and mock the body by putting the commented out line back in the test, then I'll get NullPointerException on .retrieve() in the code. Just like I'm missing an object to mock.

    I even mocked .retrieve() for requestHeadersSpec and requestBodyUriSpec:

    when(requestHeadersSpec.retrieve()).thenReturn(responseSpec);
    when(requestBodyUriSpec.retrieve()).thenReturn(responseSpec);
    

    And still no success. Any ideas what's wrong there?