Mocking Apache HTTPClient с использованием Mockito

Я пытаюсь издеваться над интерфейсом Apache HttpClient, чтобы издеваться над одним из своих методов, упомянутых ниже, чтобы вернуть в ответ объект с заглубленным JSON.

HttpResponse response = defaultHttpClient.execute(postRequest); 

Может кто-нибудь может предложить, как добиться этого с помощью некоторого примера кода? Ваша помощь будет принята с благодарностью.

Спасибо

Ответ 1

Вот что я сделал, чтобы проверить свой код с помощью Mockito и Apache HttpBuilder:

Тест класса:

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;
    }
}

Тест:

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);
    }

}

Как вы думаете, люди, мне нужно что-то улучшить? (Да, я знаю, комментарии. Это то, что я принес из своего фона Спок: D)

Ответ 2

В вашем классе unit test вам нужно высмеять defaultHttpClient:

@Mock
private HttpClient defaultHttpClient;

Затем вы скажете mockito (например, в методе @Before), чтобы на самом деле создать свои mocks:

MockitoAnnotations.initMocks(YourTestClass);

Затем в вашем методе тестирования вы определяете, что должен вернуть метод execute():

when(defaultHttpClient.execute(any()/* or wahtever you want here */)).thenReturn(stubbed JSON object);

Ответ 3

Вы можете посмотреть на HttpClientMock, я написал его для внутреннего проекта, но позже решил открыть исходный код. Это позволяет вам определять ложное поведение с помощью свободного API, а затем проверять количество совершенных вызовов. Пример:

HttpClientMock httpClientMock = new 
HttpClientMock("http://localhost:8080");
httpClientMock.onGet("/login?user=john").doReturnJSON("{permission:1}");

httpClientMock.verify().get("/login?user=john").called();

Ответ 4

Вы можете сделать это легко, используя PowerMockito, который также может легко имитировать окончательные/статические методы, частные методы и анонимные классы. Здесь пример кода для насмешливого http-запроса. JSON_STRING_DATA - это любая строка, которую вы хотите получить из метода execute.

PowerMockito.mockStatic(DefaultHttpClient.class);
    HttpClient defaultHttpClientMocked =  PowerMockito.mock(DefaultHttpClient.class);        
    PowerMockito.when(defaultHttpClientMocked.execute(Mockito.any(HttpPost.class))).thenReturn(createMockedHTTPResponse(JSON_STRING_DATA));

Ответ 5

Есть более хороший способ сделать это, не добавляя PowerMock как еще одну зависимость. Здесь вам нужен только дополнительный конструктор, принимающий HTTPClient в качестве аргумента и Mockito. В этом примере я создаю пользовательскую проверку работоспособности (Spring Actuator) и мне нужно смоделировать HTTPClient для модульного тестирования.

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

Компонент:

@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);
        }
    }
}

Метод испытания:

Обратите внимание, что здесь мы используем 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);
}