问题
I created a mock HttpsURLConnection
based on an StackExchange answer:
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 ...
}
}
However, when I use it, I'm seeing a cast exception:
java.lang.ClassCastException: sun.net.www.protocol.https.HttpsURLConnectionImpl cannot be cast to javax.net.ssl.HttpsURLConnection
The problem lies in this code:
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();
However, when I look at the source code, I see that sun.net.www.protocol.https.HttpsURLConnectionImpl
implements javax.net.ssl.HttpsURLConnection
Any suggestions on how to remedy this problem?
回答1:
The issue a conflict between the regular class loader and PowerMock's
The drawback to PowerMock is the use of a custom class loader. This class loader can modify type signatures in a way that is incompatible with the default class loader.
In certain circumstances, instantiation by reflection will cause the default class loader to be used to load a type. That class loader will not know that a type has already be loaded by PowerMock, because of the use of different signatures. The result can be casting errors for objects that should implement the cast type.
To avoid this problem, first stop PowerMock from loading javax.net.ssl.HttpsURLConnection
To prevent the cast exception, use ensure javax.net.ssl.HttpsURLConnection is only loaded by one class loader. Since I cannot stop the regular class loader from being used, the best approach is to stop the PowerMock loader from acting using the @PowerMockIgnore
annotation. E.g.
@PowerMockIgnore({"javax.net.ssl.*"})
@PrepareForTest(android.util.Log.class)
public class DialogTest {
...
The side effect is that PowerMock is no longer able to provide it's version of HttpsURLConnection
Next, expose HttpsURLConnection construction, and substitute a mock object
Introduce a factory for HttpsURLConnection. E.g.
public class HttpsUrlConnectionProvider {
public HttpsURLConnection getHttpsURLConnection(String dialogUri) throws IOException {
URL url = new URL(dialogUri);
return (HttpsURLConnection) url.openConnection();
}
}
Create a mock of the HttpsURLConnection object used for HTTP request E.g.
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);
来源:https://stackoverflow.com/questions/31157558/powermock-java-lang-classcastexception-sun-net-www-protocol-https-httpsurlconne