junitmockitojava-http-client

How to stub HttpClient send method in mockito


I am calling an external API using HttpClient as below,

   HttpClient client = HttpClient.newHttpClient();
   HttpRequest request = HttpRequest.newBuilder().uri(URI.create("http://localhost:8080/api"))
                    .POST(BodyPublishers.ofString(requestBody)).header("Authorization", 
                           authorizationHeader)
                    .header("Content-Type", "application/json").build();

   HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
   LOGGER.debug(response.body());

I have tried solution from How to mock HttpClient's send method in junit 5?. This actually does not have information on how to return the expected response in stubbing statement.

I am basically looking to stub below statement so that I can test the expected and actual result without invoking the real API,

    client.send(request, HttpResponse.BodyHandlers.ofString()); 

The real API call returns a json in String format, which I will then be mapping to an entity and use it further.

Could someone please put your thoughts on this. Thanks in advance.


Solution

  • The problem with your test is that as the HttpClient instance is created inside the method you are testing, you can't mock it. You need to change your code to be something like the code below.

    You would probably want to extend the test to make some more assertions about the parameters passed to send, and to test your handling of the response (which isn't shown in your question), but I think this answer explains how to mock the client successfully.

    package com.example.demo;
    
    import org.junit.jupiter.api.Test;
    
    import java.io.IOException;
    import java.net.URI;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse;
    
    import static org.mockito.ArgumentMatchers.any;
    import static org.mockito.Mockito.*;
    
    class MyClass {
        private final HttpClientFactory httpClientFactory;
    
        MyClass(HttpClientFactory httpClientFactory) {
            this.httpClientFactory = httpClientFactory;
        }
    
        public HttpResponse<String> myMethod(String requestBody) throws IOException, InterruptedException {
    
            HttpClient client = httpClientFactory.create();
            HttpRequest request = HttpRequest.newBuilder().uri(URI.create("http://localhost:8080/api"))
                    .POST(HttpRequest.BodyPublishers.ofString(requestBody))
                    .header("Content-Type", "application/json").build();
    
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            return response;
        }
    }
    
    class HttpClientFactory {
        public HttpClient create() {
            return HttpClient.newHttpClient();
        }
    }
    
    public class MyTest {
    
    
        @Test
        public void aTest() throws IOException, InterruptedException {
            HttpClientFactory mockFactory = mock(HttpClientFactory.class);
            HttpClient mockClient = mock(HttpClient.class);
            HttpResponse mockResponse = mock(HttpResponse.class);
            when(mockFactory.create()).thenReturn(mockClient);
            when(mockClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandler.class)))
                    .thenReturn(mockResponse);
            MyClass classToTest = new MyClass(mockFactory);
            classToTest.myMethod("the request body");
            verify(mockClient).send(argThat(request -> request.uri().equals(URI.create("http://localhost:8080/api"))), any());
        }
    }