PowerMock java.lang.ClassCastException: sun.net.www.protocol.https.HttpsURLConnectionImpl 无法转换为 javax.net.ssl.HttpsURLConnection
PowerMock java.lang.ClassCastException: sun.net.www.protocol.https.HttpsURLConnectionImpl cannot be cast to javax.net.ssl.HttpsURLConnection
我根据 StackExchange answer:
创建了一个模拟 HttpsURLConnection
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 ...
}
}
但是,当我使用它时,我看到了转换异常:
java.lang.ClassCastException: sun.net.www.protocol.https.HttpsURLConnectionImpl cannot be cast to javax.net.ssl.HttpsURLConnection
问题出在这段代码:
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();
但是,当我查看源代码时,我看到 sun.net.www.protocol.https.HttpsURLConnectionImpl
实现了 javax.net.ssl.HttpsURLConnection
关于如何解决这个问题有什么建议吗?
问题是常规 class 加载器和 PowerMock 的
之间的冲突
PowerMock 的缺点是使用自定义 class 加载程序。此 class 加载程序可以以与默认 class 加载程序不兼容的方式修改类型签名。
在某些情况下,通过反射实例化将导致使用默认的 class 加载程序来加载类型。 class 加载器将不知道 PowerMock 已经加载了一个类型,因为使用了不同的签名。结果可能是铸造错误
应该实现转换类型的对象。
为了避免这个问题,首先停止PowerMock加载javax.net.ssl.HttpsURLConnection
为防止强制转换异常,请确保 javax.net.ssl.HttpsURLConnection 仅由一个 class 加载器加载。由于我无法阻止使用常规 class 加载程序,因此最好的方法是使用 @PowerMockIgnore
注释阻止 PowerMock 加载程序运行。例如。
@PowerMockIgnore({"javax.net.ssl.*"})
@PrepareForTest(android.util.Log.class)
public class DialogTest {
...
副作用是 PowerMock 不再能够提供它的 HttpsURLConnection
版本
接下来,公开 HttpsURLConnection 构造,并替换为模拟对象
为 HttpsURLConnection 引入一个工厂。例如。
public class HttpsUrlConnectionProvider {
public HttpsURLConnection getHttpsURLConnection(String dialogUri) throws IOException {
URL url = new URL(dialogUri);
return (HttpsURLConnection) url.openConnection();
}
}
创建用于 HTTP 请求的 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);
我根据 StackExchange answer:
创建了一个模拟HttpsURLConnection
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 ...
}
}
但是,当我使用它时,我看到了转换异常:
java.lang.ClassCastException: sun.net.www.protocol.https.HttpsURLConnectionImpl cannot be cast to javax.net.ssl.HttpsURLConnection
问题出在这段代码:
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();
但是,当我查看源代码时,我看到 sun.net.www.protocol.https.HttpsURLConnectionImpl
实现了 javax.net.ssl.HttpsURLConnection
关于如何解决这个问题有什么建议吗?
问题是常规 class 加载器和 PowerMock 的
之间的冲突PowerMock 的缺点是使用自定义 class 加载程序。此 class 加载程序可以以与默认 class 加载程序不兼容的方式修改类型签名。
在某些情况下,通过反射实例化将导致使用默认的 class 加载程序来加载类型。 class 加载器将不知道 PowerMock 已经加载了一个类型,因为使用了不同的签名。结果可能是铸造错误 应该实现转换类型的对象。
为了避免这个问题,首先停止PowerMock加载javax.net.ssl.HttpsURLConnection
为防止强制转换异常,请确保 javax.net.ssl.HttpsURLConnection 仅由一个 class 加载器加载。由于我无法阻止使用常规 class 加载程序,因此最好的方法是使用 @PowerMockIgnore
注释阻止 PowerMock 加载程序运行。例如。
@PowerMockIgnore({"javax.net.ssl.*"})
@PrepareForTest(android.util.Log.class)
public class DialogTest {
...
副作用是 PowerMock 不再能够提供它的 HttpsURLConnection
接下来,公开 HttpsURLConnection 构造,并替换为模拟对象
为 HttpsURLConnection 引入一个工厂。例如。
public class HttpsUrlConnectionProvider {
public HttpsURLConnection getHttpsURLConnection(String dialogUri) throws IOException {
URL url = new URL(dialogUri);
return (HttpsURLConnection) url.openConnection();
}
}
创建用于 HTTP 请求的 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);