为何我的Apache HttpClient单元测试写法存在问题?
问题根源与解决方案
核心问题
你的CustomHttpClientImpl构造函数直接初始化了真实的CloseableHttpClient实例,导致测试中通过@Mock创建的对象无法注入到类中——测试代码里的httpClient mock完全没被用到,这才出现了两种尴尬情况:
- 注释桩代码时,调用的是真实HTTP客户端,变成了集成测试而非单元测试;
- 保留桩代码时,因为mock对象根本没参与逻辑,所以被提示“不必要的桩存根”。
解决方案:重构类以支持依赖注入
先修改CustomHttpClientImpl,添加带参数的构造函数,让依赖可以外部注入:
public class CustomHttpClientImpl implements CustomHttpClient { private final CloseableHttpClient httpClient; // 生产环境使用的无参构造 public CustomHttpClientImpl() { this(HttpClientBuilder.create().build()); } // 测试用构造,支持注入自定义的CloseableHttpClient public CustomHttpClientImpl(CloseableHttpClient httpClient) { this.httpClient = httpClient; } @Override public CloseableHttpResponse executePost(HttpPost request) throws Exception { return httpClient.execute(request); } }
修正单元测试
现在mock的CloseableHttpClient可以被正确注入,桩代码变得有意义,也不会再提示“不必要的桩存根”:
@ExtendWith(MockitoExtension.class) class CustomHttpClientImplTest { @Mock private CloseableHttpClient httpClient; private CustomHttpClientImpl customHttpClient; @BeforeEach void setUp() { // 手动注入mock对象,比@InjectMocks更直观 customHttpClient = new CustomHttpClientImpl(httpClient); } @Test void executePost_delegatesToHttpClientAndReturnsResponse() throws Exception { // 准备测试数据 HttpPost testRequest = new HttpPost("http://example.com"); CloseableHttpResponse mockResponse = Mockito.mock(CloseableHttpResponse.class); // 打桩:定义mock HttpClient的行为 Mockito.when(httpClient.execute(testRequest)).thenReturn(mockResponse); // 执行测试方法 CloseableHttpResponse result = customHttpClient.executePost(testRequest); // 断言与验证 assertNotNull(result); // 确保返回的是我们mock的响应 assertSame(mockResponse, result); // 验证HttpClient的execute方法确实被调用了一次 Mockito.verify(httpClient, Mockito.times(1)).execute(testRequest); } }
这样修改后,测试完全隔离了外部依赖,只验证CustomHttpClientImpl的委托逻辑,是标准的单元测试。
内容的提问来源于stack exchange,提问作者nevermindhf
相关产品推荐
相关产品推荐

