Java 使用Mockito模拟Apache HTTPClient

Java 使用Mockito模拟Apache HTTPClient,java,mocking,mockito,apache-httpclient-4.x,Java,Mocking,Mockito,Apache Httpclient 4.x,我试图模拟ApacheHttpClient接口,以便模拟下面提到的其中一个方法,以返回一个存根JSON对象作为响应 HttpResponse response = defaultHttpClient.execute(postRequest); 有人能建议如何通过一些示例代码实现这一点吗?非常感谢你的帮助 感谢您在单元测试课上模拟defaultHttpClient: @Mock private HttpClient defaultHttpClient; 然后告诉mockito(例如在@Bef

我试图模拟ApacheHttpClient接口,以便模拟下面提到的其中一个方法,以返回一个存根JSON对象作为响应

HttpResponse response = defaultHttpClient.execute(postRequest); 
有人能建议如何通过一些示例代码实现这一点吗?非常感谢你的帮助


感谢您在单元测试课上模拟
defaultHttpClient

@Mock
private HttpClient defaultHttpClient;
然后告诉mockito(例如在
@Before
方法中)通过以下方式实际创建模拟:

MockitoAnnotations.initMocks(YourTestClass);
然后在测试方法中定义
execute()
方法应返回的内容:

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

您可以使用PowerMockito轻松地实现这一点,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));

下面是我使用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);
    }

}
测试:

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)

你可以看看,我是为内部项目写的,但后来决定开源。它允许您使用fluentapi定义模拟行为,并在稍后验证许多调用。例如:

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

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

有一种更好的方法可以做到这一点,而不必添加PowerMock作为另一个依赖项。在这里,您只需要一个额外的构造函数,将HTTPClient作为参数和Mockito。在这个例子中,我创建了一个定制的健康检查(Spring Actuator),我需要模拟HTTPClient进行单元测试

Libs:junit5、springboot2.1.2和mockito2

组件:

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

}
@组件
公共类MyHealthCheck扩展了AbstractHealthIndicator{
HttpClient-HttpClient;
公共MyHealthCheck(){
httpClient=HttpClientBuilder.create().build();
}
/** 
使用HttpClient参数向类中添加了另一个构造函数。
这个可以用来测试
*/ 
公共MyHealthCheck(HttpClient HttpClient){
this.httpClient=httpClient;
}
/**
测试方法
*/ 
@凌驾
受保护的void doHealthCheck(生成器生成器)引发异常{
//
//执行请求并获取状态代码
HttpGet请求=新建HttpGet(“http://www.SomeAuthEndpoint.com");
HttpResponse response=httpClient.execute(请求);
//
//根据状态代码更新生成器
int statusCode=response.getStatusLine().getStatusCode();
如果(状态代码==200 | |状态代码==401){
builder.up().withDetail(“服务代码”,状态代码);
}否则{
builder.unknown().withDetail(“服务代码”,状态代码);
}
}
}
试验方法:

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

}
注意,这里我们使用Mockito.any(HttpGet.class)

私有静态HttpClient-HttpClient;
私有静态HttpResponse HttpResponse;
私有静态StatusLine StatusLine;
@以前
公共静态void init(){
//
//给定
httpClient=Mockito.mock(httpClient.class);
httpResponse=Mockito.mock(httpResponse.class);
statusLine=Mockito.mock(statusLine.class);
}
@试验
public void doHealthCheck\u endReturns401\u shouldReturnUp()引发异常{
//
//什么时候
当(statusLine.getStatusCode())。然后返回(401);
when(httpResponse.getStatusLine())。然后return(statusLine);
when(httpClient.execute(Mockito.any(HttpGet.class))。然后返回(httpResponse);
//
//然后
MyHealthCheck MyHealthCheck=新的MyHealthCheck(httpClient);
Health.Builder=新的Health.Builder();
myHealthCheck.doHealthCheck(生成器);
Status Status=builder.build().getStatus();
Assertions.assertTrue(Status.UP==Status);
}

谢谢,太棒了!但我还需要知道的是,我应该模拟DefaultHttpClient还是HttpClient?在mockito中,您可以模拟所有:接口、抽象类和普通类。在您的情况下,如果只想模拟
execute()
方法的行为,可以模拟
HttpClient
接口。但是如果您想模拟
DefaultHttpClient
中的方法,而这些方法在
HttpClient
中不可用,那么您必须直接模拟
DefaultHttpClient
类;BufferedReader rd=新的BufferedReader(新的InputStreamReader(response.getEntity().getContent());StringBuffer结果=新的StringBuffer();而((line=rd.readLine())!=null){result.append(line);}JSONObject jsonResponseObject=new JSONObject(result.toString())-1.httpClient.execute()不返回JSON对象,而是一个响应对象,您需要从中提取JSON主体,这就是为什么模拟它不是直接进行的。这看起来很好,但是您如何确保被测试的类使用
HttpClientMock
而不是
HttpClientMock.createDefault()
?嗯,不同的应用程序之间可能会有很大的不同。在我的应用程序中,通常我有一个单独的测试配置,它将HttpClientMock注入到应用程序上下文中。让我们打赌:3年后PowerMockito将不再流行,使用它的任何测试都将是“遗留”测试(可能是@Ignore'd,或者需要维护的负担,或者您的团队将试图迁移掉它)。如果我错了,请大家在2021年7月回复我,我会谦恭地吃馅饼。这很好,但有没有一种方法可以做到这一点,在这种情况下,您的模拟不需要如此具体的实现?例如,如果我们将HTTP客户机切换到具有不同接口的东西,那么测试仍然可以工作?理想情况下,只需监视HTTP请求发出(并监视URL、头、负载等内容)和控制返回响应的能力(以及监视