I'm trying to unit test some API calls using MockWebServer and Robolectric.
My test class is annotated with:
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 23)
However when trying to build the Retrofit instance I get the following exception:
java.lang.NullPointerException
at android.os.Handler.__constructor__(Handler.java:229)
at android.os.Handler.<init>(Handler.java)
at retrofit2.Platform$Android$MainThreadExecutor.<init>(Platform.java:105)
at retrofit2.Platform$Android.defaultCallbackExecutor(Platform.java:97)
at retrofit2.Retrofit$Builder.build(Retrofit.java:556)
The code I'm using to build the retrofit instance:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(mMockServer.url(""))
.addConverterFactory(GsonConverterFactory.create())
.build();
The exception above is returned upon calling .build()
.
How do I fix this problem?
I had this same problem. There is a bug open about the underlying cause here but while that is ongoing, I settled for using Robolectric 3.0 and the solution outlined by dave-r12 here which is to create the mock I've included below.
@RunWith(RobolectricGradleTestRunner.class)
@Config(shadows = CreateOkHttpClientTest.MyNetworkSecurityPolicy.class)
public class CreateOkHttpClientTest {
@Test
...
@Implements(NetworkSecurityPolicy.class)
public static class MyNetworkSecurityPolicy {
@Implementation
public static NetworkSecurityPolicy getInstance() {
try {
Class<?> shadow = MyNetworkSecurityPolicy.class.forName("android.security.NetworkSecurityPolicy");
return (NetworkSecurityPolicy) shadow.newInstance();
} catch (Exception e) {
throw new AssertionError();
}
}
@Implementation
public boolean isCleartextTrafficPermitted() {
return true;
}
}
}
An alternate solution that seems to work is to just provide a dummy executor when setting up Retrofit
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://www.example.com/")
.client(client)
.callbackExecutor(new Executor() {
@Override
public void execute(Runnable command) {
//doesn't matter since tests are synchronous
}
})
.addConverterFactory(ScalarsConverterFactory.create())
.build();
It may be something that in the end needs to be corrected on the Retrofit side, somehow detecting if the current Platform is Robolectric and return a dummy executor or something.
If you are stuck on Robolectric 3 or below and you are targeting the API 25 the proper solution is almost the same like accepted one.
@Implements(NetworkSecurityPolicy.class)
public class NetworkSecurityPolicyShadow {
@Implementation
public static NetworkSecurityPolicy getInstance() {
try {
Class<?> shadow = Class.forName("android.security.NetworkSecurityPolicy");
return (NetworkSecurityPolicy) shadow.newInstance();
} catch (Exception e) {
throw new AssertionError();
}
}
@Implementation
public boolean isCleartextTrafficPermitted(String host) {
return true;
}
}
Only difference is in isCleartextTrafficPermitted(String host)
because the OkHttp will try to call this method with host
argument.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With