You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JUnit中Mock嵌套对象:自定义CloseableHttpResponse返回结果

解决方法:Mock内部创建的CloseableHttpClient的execute方法

因为你的doRequest方法是内部直接创建CloseableHttpClient实例,Mockito无法直接Mock局部变量,所以必须先调整代码结构,让HttpClient的实例可被Mock拦截。下面提供两种可行方案:

方案一:将CloseableHttpClient改为依赖注入(推荐)

把HttpClient作为MyService的依赖注入进去,而非在方法内硬创建,这样测试时可以直接Mock这个依赖。

1. 修改MyService代码

public class MyService {
    private final CloseableHttpClient httpClient;

    // 构造方法注入HttpClient,生产环境可传入默认实例:HttpClientBuilder.create().build()
    public MyService(CloseableHttpClient httpClient) {
        this.httpClient = httpClient;
    }

    public String doRequest(HttpUriRequest request) throws IOException {
        try (CloseableHttpResponse response = httpClient.execute(request)) {
            // 响应解析逻辑示例
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity);
        }
    }
}

2. 编写JUnit测试代码

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.HttpStatus;
import org.apache.http.entity.StringEntity;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.assertEquals;

@ExtendWith(MockitoExtension.class)
public class MyServiceTest {
    @Mock
    private CloseableHttpClient mockHttpClient;

    @InjectMocks
    private MyService myService;

    @Test
    void doRequest_ShouldReturnExpectedResponse() throws IOException {
        // 1. 构造预设响应内容
        String expectedResponse = "Mocked Response Content";
        CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class);
        HttpEntity mockEntity = new StringEntity(expectedResponse);

        // 2. Mock响应的核心方法
        when(mockResponse.getStatusLine()).thenReturn(new BasicStatusLine(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"));
        when(mockResponse.getEntity()).thenReturn(mockEntity);
        doNothing().when(mockResponse).close(); // Mock关闭方法避免异常

        // 3. Mock HttpClient的execute方法,返回预设响应
        HttpUriRequest request = new HttpGet("http://example.com");
        when(mockHttpClient.execute(request)).thenReturn(mockResponse);

        // 4. 执行测试并断言
        String actualResponse = myService.doRequest(request);
        assertEquals(expectedResponse, actualResponse);
    }
}

方案二:抽离HttpClient创建方法(适合不愿修改依赖结构的场景)

如果不想给MyService添加构造注入,可以把创建HttpClient的逻辑抽成protected方法,测试时用Mockito的Spy来Mock这个方法,返回Mock的HttpClient实例。

1. 修改MyService代码

public class MyService {
    public String doRequest(HttpUriRequest request) throws IOException {
        try (CloseableHttpClient httpClient = createHttpClient();
             CloseableHttpResponse response = httpClient.execute(request)) {
            // 响应解析逻辑示例
            HttpEntity entity = response.getEntity();
            return EntityUtils.toString(entity);
        }
    }

    // 抽离创建逻辑,protected方便测试Mock
    protected CloseableHttpClient createHttpClient() {
        return HttpClientBuilder.create().build();
    }
}

2. 编写JUnit测试代码

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.assertEquals;

@ExtendWith(MockitoExtension.class)
public class MyServiceTest {
    @Spy
    private MyService myService;

    @Test
    void doRequest_ShouldReturnMockedResponse() throws IOException {
        // 1. 创建Mock的HttpClient和预设响应
        CloseableHttpClient mockHttpClient = mock(CloseableHttpClient.class);
        CloseableHttpResponse mockResponse = mock(CloseableHttpResponse.class);
        String expectedContent = "Mocked Response";
        HttpEntity mockEntity = new StringEntity(expectedContent);

        // 2. Mock createHttpClient方法,返回自定义的Mock实例
        doReturn(mockHttpClient).when(myService).createHttpClient();

        // 3. Mock execute方法返回预设响应
        HttpUriRequest request = new HttpGet("http://example.com");
        when(mockHttpClient.execute(request)).thenReturn(mockResponse);

        // 4. Mock响应的实体和关闭方法
        when(mockResponse.getEntity()).thenReturn(mockEntity);
        doNothing().when(mockResponse).close();

        // 5. 执行测试并断言
        String actualContent = myService.doRequest(request);
        assertEquals(expectedContent, actualContent);
    }
}

关键注意点

  • 必须Mock CloseableHttpResponse的close()方法,否则测试时可能因资源未正确关闭抛出异常
  • 如果响应解析逻辑用到状态码、响应头,需额外Mock对应方法(如getStatusLine())
  • 优先选择方案一,依赖注入更符合面向对象设计原则,测试逻辑也更清晰

内容的提问来源于stack exchange,提问作者Timothy Clotworthy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 07:20:33