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
相关产品推荐
相关产品推荐

