Mocking Apache HTTPClient using Mockito

后端 未结 5 788
走了就别回头了
走了就别回头了 2020-12-25 11:24

I\'m trying to mock Apache HttpClient Interface in order to mock one of its methods mentioned below to return a stubbed JSON object in response.

HttpResponse         


        
相关标签:
5条回答
  • 2020-12-25 11:43

    There is a nicer way to do this without having to add PowerMock as yet another dependency. Here you only need an extra constructor taking HTTPClient as an argument and Mockito. In this example I'm creating a custom health check (Spring Actuator) and I need to mock the HTTPClient for unit testing.

    Libs: JUnit 5, Spring Boot 2.1.2 and Mockito 2.

    Component:

    @Component
    public class MyHealthCheck extends AbstractHealthIndicator {
    
        HttpClient httpClient;
    
        public MyHealthCheck() { 
            httpClient = HttpClientBuilder.create().build();
        }
    
        /** 
        Added another constructor to the class with an HttpClient argument.
        This one can be used for testing
        */ 
        public MyHealthCheck(HttpClient httpClient) { 
            this.httpClient = httpClient; 
        }
    
        /**
        Method to test 
        */ 
        @Override
        protected void doHealthCheck(Builder builder) throws Exception {
    
            //
            // Execute request and get status code
            HttpGet request = new HttpGet("http://www.SomeAuthEndpoint.com");
            HttpResponse response = httpClient.execute(request);
    
            //
            // Update builder according to status code
            int statusCode = response.getStatusLine().getStatusCode();
            if(statusCode == 200 || statusCode == 401) {
                builder.up().withDetail("Code from service", statusCode);
            } else {
                builder.unknown().withDetail("Code from service", statusCode);
            }
        }
    }
    

    Test method:

    Note that here we use Mockito.any(HttpGet.class)

    private static HttpClient httpClient;
    private static HttpResponse httpResponse;
    private static StatusLine statusLine;
    
    @BeforeAll
    public static void init() {
        //
        // Given
        httpClient = Mockito.mock(HttpClient.class);
        httpResponse = Mockito.mock(HttpResponse.class);
        statusLine = Mockito.mock(StatusLine.class);
    }
    
    
    @Test
    public void doHealthCheck_endReturns401_shouldReturnUp() throws Exception {
    
        //
        // When
        when(statusLine.getStatusCode()).thenReturn(401);
        when(httpResponse.getStatusLine()).thenReturn(statusLine);
        when(httpClient.execute(Mockito.any(HttpGet.class))).thenReturn(httpResponse);
    
        //
        // Then
        MyHealthCheck myHealthCheck = new MyHealthCheck(httpClient);
        Health.Builder builder = new Health.Builder();
        myHealthCheck.doHealthCheck(builder);
        Status status = builder.build().getStatus();
        Assertions.assertTrue(Status.UP == status);
    }
    
    0 讨论(0)
  • 2020-12-25 11:55

    Here is what I did to test my code using Mockito and Apache HttpBuilder:

    Class under test:

    import java.io.BufferedReader;
    import java.io.IOException;
    
    import javax.ws.rs.core.Response.Status;
    
    import org.apache.http.HttpResponse;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.HttpClientBuilder;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    public class StatusApiClient {
    private static final Logger LOG = LoggerFactory.getLogger(StatusApiClient.class);
    
        private String targetUrl = "";
        private HttpClient client = null;
        HttpGet httpGet = null;
    
        public StatusApiClient(HttpClient client, HttpGet httpGet) {
            this.client = client;
            this.httpGet = httpGet;
        }
    
        public StatusApiClient(String targetUrl) {
            this.targetUrl = targetUrl;
            this.client = HttpClientBuilder.create().build();
            this.httpGet = new HttpGet(targetUrl);
        }
    
        public boolean getStatus() {
            BufferedReader rd = null;
            boolean status = false;
            try{
                LOG.debug("Requesting status: " + targetUrl);
    
    
                HttpResponse response = client.execute(httpGet);
    
                if(response.getStatusLine().getStatusCode() == Status.OK.getStatusCode()) {
                    LOG.debug("Is online.");
                    status = true;
                }
    
            } catch(Exception e) {
                LOG.error("Error getting the status", e);
            } finally {
                if (rd != null) {
                    try{
                        rd.close();
                    } catch (IOException ioe) {
                        LOG.error("Error while closing the Buffered Reader used for reading the status", ioe);
                    }
                }   
            }
    
            return status;
        }
    }
    

    Test:

    import java.io.IOException;
    
    import org.apache.http.HttpResponse;
    import org.apache.http.StatusLine;
    import org.apache.http.client.ClientProtocolException;
    import org.apache.http.client.HttpClient;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.conn.HttpHostConnectException;
    import org.junit.Assert;
    import org.junit.Test;
    import org.mockito.Mockito;
    
    public class StatusApiClientTest extends Mockito {
    
        @Test
        public void should_return_true_if_the_status_api_works_properly() throws ClientProtocolException, IOException {
            //given:
            HttpClient httpClient = mock(HttpClient.class);
            HttpGet httpGet = mock(HttpGet.class);
            HttpResponse httpResponse = mock(HttpResponse.class);
            StatusLine statusLine = mock(StatusLine.class);
    
            //and:
            when(statusLine.getStatusCode()).thenReturn(200);
            when(httpResponse.getStatusLine()).thenReturn(statusLine);
            when(httpClient.execute(httpGet)).thenReturn(httpResponse);
    
            //and:
            StatusApiClient client = new StatusApiClient(httpClient, httpGet);
    
            //when:
            boolean status = client.getStatus();
    
            //then:
            Assert.assertTrue(status);
        }
    
        @Test
        public void should_return_false_if_status_api_do_not_respond() throws ClientProtocolException, IOException {
            //given:
            HttpClient httpClient = mock(HttpClient.class);
            HttpGet httpGet = mock(HttpGet.class);
            HttpResponse httpResponse = mock(HttpResponse.class);
            StatusLine statusLine = mock(StatusLine.class);
    
            //and:
            when(httpClient.execute(httpGet)).thenThrow(HttpHostConnectException.class);
    
            //and:
            StatusApiClient client = new StatusApiClient(httpClient, httpGet);
    
            //when:
            boolean status = client.getStatus();
    
            //then:
            Assert.assertFalse(status);
        }
    
    }
    

    What do you think folks, do I need to improve something? (Yeah, I know, the comments. That is something I brought from my Spock background :D)

    0 讨论(0)
  • 2020-12-25 12:04

    You can look at HttpClientMock, I wrote it for internal project but later decided to open source. It allows you to define mock behavior with fluent API and later verify a number of made calls. Example:

    HttpClientMock httpClientMock = new 
    HttpClientMock("http://localhost:8080");
    httpClientMock.onGet("/login?user=john").doReturnJSON("{permission:1}");
    
    httpClientMock.verify().get("/login?user=john").called();
    
    0 讨论(0)
  • 2020-12-25 12:04

    You can do this easily using PowerMockito which can also mock final/static methods, private methods and anonymous classes easily. Here's the sample code for mocking http request. JSON_STRING_DATA is any string which you want to get from execute method.

    PowerMockito.mockStatic(DefaultHttpClient.class);
        HttpClient defaultHttpClientMocked =  PowerMockito.mock(DefaultHttpClient.class);        
        PowerMockito.when(defaultHttpClientMocked.execute(Mockito.any(HttpPost.class))).thenReturn(createMockedHTTPResponse(JSON_STRING_DATA));
    
    0 讨论(0)
  • 2020-12-25 12:05

    In your unit test class you need to mock defaultHttpClient:

    @Mock
    private HttpClient defaultHttpClient;
    

    Then you tell mockito (for example in @Before method) to actually create your mocks by:

    MockitoAnnotations.initMocks(YourTestClass);
    

    Then in your test method you define what execute() method should return:

    when(defaultHttpClient.execute(any()/* or wahtever you want here */)).thenReturn(stubbed JSON object);
    
    0 讨论(0)
提交回复
热议问题