Mockito测试报错:SSLConnectionSocketFactory无法返回,仅用MockitoExtension求解
问题与解决方案
问题
编写了执行HTTP POST操作的Java代码,其中PostUtility类的getSocketFactory方法返回SSLSocketFactory。使用MockitoExtension编写单元测试时出现错误:提示“SSLConnectionSocketFactory$MockitoMock$xxx无法被getSocketFactory()返回,该方法应返回SSLSocketFactory”。要求仅用MockitoExtension解决,不使用PowerMockito。
业务代码
public Response postResponse( @NonNull final String payload, @NonNull final String endpoint, @NonNull final ContentType contentType, @NonNull final String clientCertificate ) { SSLConnectionSocketFactory sslConnectionSocketFactory; if (StringUtils.isNotEmpty(clientCertificate)) { // Client certificate is present, thus using TLS sslConnectionSocketFactory = new SSLConnectionSocketFactory(postUtility .getSocketFactory(clientCertificate), NoopHostnameVerifier.INSTANCE); } else { SSLContext sslcontext = SSLContext.getInstance("TLS"); sslcontext.init(null, null, new java.security.SecureRandom()); sslConnectionSocketFactory = new SSLConnectionSocketFactory( sslcontext, new String[]{"TLSv1"}, //{ "TLS10" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier()); } HttpPost httpPost = new HttpPost(endpoint); httpPost.setHeader("Content-Type", contentType.getMimeType()); httpPost.setEntity( EntityBuilder.create() .setText(payload) .setContentType(contentType) .build() ); try ( CloseableHttpClient httpclient = HttpClients.custom() .setSSLContext(SSLContext.getDefault()) .setSSLSocketFactory(sslConnectionSocketFactory) .build(); CloseableHttpResponse response = httpclient.execute(httpPost) ) { return Response.status(response.getStatusLine().getStatusCode()).build(); } catch (Exception ex) { log.error("Unable to send response", ex); return Response.serverError().build(); } }
原测试代码(存在错误)
@Mock private PostUtility postUtility; @Mock private SSLConnectionSocketFactory sslConnectionSocketFactory; @Mock private SSLSocketFactory sslSocketFactory; @Mock private CloseableHttpClient httpclient; @Mock private CloseableHttpResponse httpResponse; @Mock private StatusLine statusLine; @Test public void testPostResponse() throws Exception { when(postUtility.getSocketFactory(clientCertificate)) .thenReturn(sslSocketFactory); when(new SSLConnectionSocketFactory(postUtility.getSocketFactory(clientCertificate), NoopHostnameVerifier.INSTANCE)) .thenReturn(sslConnectionSocketFactory); when(httpResponse.getStatusLine()).thenReturn(statusLine); when(statusLine.getStatusCode()).thenReturn(200); when(httpclient.execute(any(HttpPost.class))).thenReturn(httpResponse); Response response = className.postResponse("testPayLoad","testEndPoint",ContentType.TEXT_XML, "testclientCertificate"); assertEquals(200, response.getStatus(), "Response status code should be 200"); }
错误原因
原测试代码中when(new SSLConnectionSocketFactory(...)).thenReturn(...)是无效操作——Mockito无法直接mock构造方法的调用,这行代码会导致类型匹配错误;同时业务代码中是直接新建SSLConnectionSocketFactory实例,无法直接mock该对象。
修复方案
关键修复点
- 删除mock构造方法的错误代码,改用Mockito的静态mock能力控制
HttpClients的创建流程 - 确保
PostUtility.getSocketFactory返回的mock对象类型与方法声明一致(SSLSocketFactory) - mock
HttpClientBuilder的链式调用,让其最终返回预先定义好的CloseableHttpClientmock对象
修正后的测试代码
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(MockitoExtension.class) public class YourServiceTest { // 替换为实际测试类名 @Mock private PostUtility postUtility; @Mock private SSLSocketFactory sslSocketFactory; @Mock private CloseableHttpClient httpclient; @Mock private CloseableHttpResponse httpResponse; @Mock private StatusLine statusLine; @Mock private HttpClientBuilder httpClientBuilder; private static final String TEST_CERT = "testclientCertificate"; @Test public void testPostResponse() throws Exception { // 1. Mock PostUtility返回正确的SSLSocketFactory when(postUtility.getSocketFactory(TEST_CERT)) .thenReturn(sslSocketFactory); // 2. 静态mock HttpClients.custom(),返回我们的HttpClientBuilder mock try (MockedStatic<HttpClients> mockedHttpClients = mockStatic(HttpClients.class)) { mockedHttpClients.when(HttpClients::custom).thenReturn(httpClientBuilder); // 3. Mock HttpClientBuilder的链式调用,保证每个set方法返回自身,最终build返回mock的client when(httpClientBuilder.setSSLContext(any(SSLContext.class))).thenReturn(httpClientBuilder); when(httpClientBuilder.setSSLSocketFactory(any(SSLConnectionSocketFactory.class))).thenReturn(httpClientBuilder); when(httpClientBuilder.build()).thenReturn(httpclient); // 4. Mock HttpClient执行结果 when(httpclient.execute(any(HttpPost.class))).thenReturn(httpResponse); when(httpResponse.getStatusLine()).thenReturn(statusLine); when(statusLine.getStatusCode()).thenReturn(200); // 执行测试方法(替换为实际业务类的实例化方式) YourService service = new YourService(postUtility); Response response = service.postResponse("testPayLoad", "testEndPoint", ContentType.TEXT_XML, TEST_CERT); // 断言结果 assertEquals(200, response.getStatus(), "响应状态码应为200"); // 验证关键调用 verify(postUtility).getSocketFactory(TEST_CERT); mockedHttpClients.verify(HttpClients::custom); verify(httpClientBuilder).setSSLSocketFactory(any(SSLConnectionSocketFactory.class)); verify(httpclient).execute(any(HttpPost.class)); } } }
依赖要求
确保测试依赖中引入mockito-inline(用于支持静态mock),Maven依赖示例:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <version>4.11.0</version> <!-- 使用最新稳定版本 --> <scope>test</scope> </dependency>
内容的提问来源于stack exchange,提问作者snigdha singh
相关产品推荐
相关产品推荐

