Java 如何使用junit对HttpClient重试逻辑进行单元测试

Java 如何使用junit对HttpClient重试逻辑进行单元测试,java,junit,mockito,apache-httpclient-4.x,powermock,Java,Junit,Mockito,Apache Httpclient 4.x,Powermock,我正在使用ApacheHTTP客户端来使用服务,我需要根据超时和响应代码重试请求。 为此,我实现了如下代码。如何为超时和响应代码场景的重试逻辑编写junit测试。我希望以这样的方式编写单元测试:当我发送任何post/get请求时,如果它返回429错误代码响应或任何TimeOutException,我应该确保正确执行重试逻辑。我不知道如何为重试逻辑编写单元测试。 通过谷歌搜索,我找到了下面的链接,但它对我没有帮助 我正在使用junit、Mockito编写单元测试,并使用PowerMock来模拟静

我正在使用ApacheHTTP客户端来使用服务,我需要根据超时和响应代码重试请求。 为此,我实现了如下代码。如何为超时和响应代码场景的重试逻辑编写junit测试。我希望以这样的方式编写单元测试:当我发送任何post/get请求时,如果它返回429错误代码响应或任何TimeOutException,我应该确保正确执行重试逻辑。我不知道如何为重试逻辑编写单元测试。 通过谷歌搜索,我找到了下面的链接,但它对我没有帮助

我正在使用junit、Mockito编写单元测试,并使用PowerMock来模拟静态方法

public class GetClient {

private static CloseableHttpClient httpclient;

public static CloseableHttpClient getInstance() {

        try {
            HttpClientBuilder builder = HttpClients.custom().setMaxConnTotal(3)
                    .setMaxConnPerRoute(3);
            builder.setRetryHandler(retryHandler());
            builder.setServiceUnavailableRetryStrategy(new ServiceUnavailableRetryStrategy() {
            int waitPeriod = 200;

            @Override
            public boolean retryRequest(final HttpResponse response, final int executionCount,
                final HttpContext context) {

                int statusCode = response.getStatusLine().getStatusCode();
                return (((statusCode == 429) || (statusCode >= 300 && statusCode <= 399))
                            && (executionCount < 3));
            }

            @Override
            public long getRetryInterval() {
                return waitPeriod;
            }
            });            

            httpclient = builder.build();

        } catch (Exception e) {
            //handle exception
        }
        return httpclient;
    }

     private static HttpRequestRetryHandler retryHandler() {
        return (exception, executionCount, context) -> {
            if (executionCount > maxRetries) {
                // Do not retry if over max retry count
                return false;
            }
            if (exception instanceof InterruptedIOException) {                
                // Timeout
                return true;
            }
            if (exception instanceof UnknownHostException) {
                // Unknown host
                return false;
            }
            if (exception instanceof ConnectTimeoutException) {
                // Connection refused
                return false;
            }
            if (exception instanceof SSLException) {
                // SSL handshake exception
                return false;
            }
            HttpClientContext clientContext = HttpClientContext.adapt(context);
            HttpRequest request = clientContext.getRequest();
            boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
            if (idempotent) {
                // Retry if the request is considered idempotent
                return true;
            }
            return false;
        };
    }
}

public CloseableHttpResponse uploadFile(){    
        CloseableHttpClient httpClient = GetClient.getInstance();
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(post);
        } catch (Exception ex) {
            //handle exception
        }
        return response;    
   }
public类GetClient{
私有静态可关闭httpclient httpclient;
公共静态CloseableHttpClient getInstance(){
试一试{
HttpClientBuilder builder=HttpClients.custom().SetMaxConntTotal(3)
.setMaxConnPerRoute(3);
setRetryHandler(retryHandler());
builder.setServiceUnavailableRetryStrategy(新ServiceUnavailableRetryStrategy(){
int waitPeriod=200;
@凌驾
公共布尔retryRequest(最终HttpResponse响应,最终int executionCount,
最终HttpContext(上下文){
int statusCode=response.getStatusLine().getStatusCode();
返回((statusCode==429)| |(statusCode>=300&&statusCode){
如果(executionCount>maxRetries){
//如果超过最大重试次数,请不要重试
返回false;
}
if(InterruptedIOException的异常实例){
//超时
返回true;
}
if(未知后异常的异常实例){
//未知宿主
返回false;
}
if(ConnectTimeoutException的异常实例){
//拒绝连接
返回false;
}
if(SSLexException的异常实例){
//SSL握手异常
返回false;
}
HttpClientContext=HttpClientContext.adapt(上下文);
HttpRequest请求=clientContext.getRequest();
布尔幂等元=!(HttpEntityEnclosingRequest的请求实例);
if(幂等){
//如果请求被认为是幂等的,请重试
返回true;
}
返回false;
};
}
}
public CloseableHttpResponse uploadFile(){
CloseableHttpClient httpClient=GetClient.getInstance();
CloseableHttpResponse响应=null;
试一试{
response=httpClient.execute(post);
}捕获(例外情况除外){
//处理异常
}
返回响应;
}
有人能帮我一下吗。

您的httpClient有一个“目标”url,比如localhost:1234。您要测试的是重试代码,所以您不应该触碰httpClient本身(因为它不是您的组件,您不应该也测试它)

因此,当前的问题是当您的localhost:1234响应出现问题时,您希望看到将运行的重试逻辑(而不是您的实现..如果它没有使用正确的配置运行,那么这就是问题所在..您唯一需要做的就是模拟“localhost:1234”

此工具是实现此目的的最佳选择。您可以为目标url创建存根,并根据您喜欢的任何内容给出一系列响应

您的代码应该如下所示 调用
uploadFile

stubFor(post(urlEqualTo(“/hash”))
.willReturn(aResponse()
.withStatus(200)
.与身体(外部反应));
及 调用
uploadFile

和验证步骤,以验证到达模拟端点的模拟请求

Assert.Assert*/…您想在处理程序/code/resposnes中断言的任何内容
验证(postRequestedFor(urlEqualTo(“/hash”));

你是如何为这个类编写单元测试的?你有没有成功调用
uploadFile
的测试?我还想看看
uploadFile()
的一个简单运行测试。首先,这里的结构经常调用静态项,不利于编写单元测试,而PowerMock不是我的朋友(除了白盒)。我的总体策略是将httpclient构建为一个模拟,为适当的方法抛出一个异常(例如,使用
Mockito.when(…)。然后抛出(…)。然后返回(…)
),并让模拟使用
Mockito.doCallRealMethod()
了解正确的方法。但总而言之,没有看到
上传文件
方法的工作版本这只是一个猜测。本指南是我在wiremock上的最爱之一。看一看,让我知道你是否同意。对于多个响应,只需在存根上重复
将返回
方法。它将在同一个wa中工作y作为junit。每次调用存根时,它都会提供下一行willReturn。wiremock服务器将运行在类似localhost:9999的环境中。这在urlEqualsTo()中被省略,需要提供给代码一些配置(因为url不应该硬编码,应该已经实现以注入配置)尾随url/fetch/token可能是硬编码的,因为它是项目的核心路径,可以按原样使用。因此urlEqualsTo(“/fetch/token”)。您应该在测试中使用绝对路径,并定义所有内容以减少无意义/错误的测试。因此,请使用urlEqualsTo(“/fetch/token”)启动wiremock.stub添加2-3将返回运行您的测试,然后进行验证!JaCoCo不应该关心您的测试框架(wiremock等),因为它会增强您创建覆盖率的实际代码。我没有使用