PowerMock java.lang.ClassCastException:sun.net.www.protocol.https.HttpsURLConnectionImpl无法强制转换为javax.net.ssl.HttpsURLConnection

Don*_*rty 4 java powermock powermockito

HttpsURLConnection根据StackExchange答案创建了一个模拟:

import java.net.URL;
import javax.net.ssl.HttpsURLConnection;

...

@RunWith(PowerMockRunner.class)
public class DialogTest {
    public void mockHttpsUrlConnectionExample() throws Exception
    {
        URL mockUrl = PowerMockito.mock(URL.class);
        PowerMockito.whenNew(URL.class).withAnyArguments().thenReturn(mockUrl);
        HttpsURLConnection mockUrlConnection = PowerMockito.mock(HttpsURLConnection.class);
        PowerMockito.when(mockUrl.openConnection()).thenReturn(mockUrlConnection);
        PowerMockito.when(mockUrlConnection.getResponseCode()).thenReturn(200);

        // Create and call my objects ...

    }
}
Run Code Online (Sandbox Code Playgroud)

但是,当我使用它时,我看到一个强制转换异常:

java.lang.ClassCastException: sun.net.www.protocol.https.HttpsURLConnectionImpl cannot be cast to javax.net.ssl.HttpsURLConnection
Run Code Online (Sandbox Code Playgroud)

问题出在这个代码中:

import java.net.URL;
import javax.net.ssl.HttpsURLConnection;

...

private Boolean sendRequest(String endpoint, JSONObject requestData, Boolean throwOnAuthException) throws JSONException, IOException {
    this.responseData = null;

    try {
        String serviceURI = getServiceURI();
        String dialogUri = String.format("%s%s", serviceURI, endpoint);
        URL url = new URL(dialogUri);

        // Exception source is this cast
        HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
Run Code Online (Sandbox Code Playgroud)

但是,当我查看源代码时,我看到了这个sun.net.www.protocol.https.HttpsURLConnectionImpl实现javax.net.ssl.HttpsURLConnection

有关如何解决此问题的任何建议?

Don*_*rty 6

普通类加载器和PowerMock之间的冲突问题

PowerMock的缺点是使用自定义类加载器.此类加载器可以以与默认类加载器不兼容的方式修改类型签名.

在某些情况下,通过反射进行实例化将导致使用默认类加载器来加载类型.由于使用了不同的签名,类加载器不会知道PowerMock已经加载了一个类型.结果可能是应该实现强制转换类型的对象的转换错误.

要避免此问题,请先停止加载PowerMock javax.net.ssl.HttpsURLConnection

要防止强制转换异常,请确保javax.net.ssl.HttpsURLConnection仅由一个类加载器加载.由于我无法停止使用常规类加载器,因此最好的方法是阻止PowerMock加载器使用@PowerMockIgnore注释.例如

@PowerMockIgnore({"javax.net.ssl.*"})
@PrepareForTest(android.util.Log.class)
public class DialogTest {
...
Run Code Online (Sandbox Code Playgroud)

副作用是PowerMock不再能够提供它的版本 HttpsURLConnection

接下来,公开HttpsURLConnection构造,并替换模拟对象

为HttpsURLConnection引入工厂.例如

public class HttpsUrlConnectionProvider {
    public HttpsURLConnection getHttpsURLConnection(String dialogUri) throws IOException {
        URL url = new URL(dialogUri);
        return (HttpsURLConnection) url.openConnection();
    }
}
Run Code Online (Sandbox Code Playgroud)

创建用于HTTP请求Eg的HttpsURLConnection对象的模拟

final HttpsURLConnection mockUrlConnection = PowerMockito.mock(HttpsURLConnection.class);
PowerMockito.when(mockUrlConnection, "getResponseCode").thenReturn(200);
PowerMockito.when(mockUrlConnection, "getOutputStream").thenReturn(outputStream);

// Replace the HttpsURLConnection factory with one that returns our mock HttpsURLConnection
HttpsUrlConnectionProvider mockConnFactory = new HttpsUrlConnectionProvider() {
    public HttpsURLConnection getHttpsURLConnection(String dialogUri) throws
            IOException {
       return mockUrlConnection;
    }
};
dialog.setHttpsUrlConnectionProvider(mockConnFactory);
Run Code Online (Sandbox Code Playgroud)

  • 我有一个相关的问题和`@PowerMockIgnore({"javax.net.ssl.*"})`对我来说足够了:) (2认同)