7

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?

user_4685247
  • 2,878
  • 2
  • 17
  • 43

4 Answers4

7

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;
        }
    }

}
Nick Cardoso
  • 20,807
  • 14
  • 73
  • 124
1

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.

Jawnnypoo
  • 993
  • 12
  • 18
1

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.

jakubbialkowski
  • 1,546
  • 16
  • 24
0

Could you try with sdk=21. I believe support for sdk=23 was added only from 3.1-SNAPSHOT and not release version.

Edit: I was wrong support was added from version 3.1 because I could successfully run a test

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 23)
public class ExampleUnitTest {

private MockWebServer mMockServer = new MockWebServer();

    @Test
    public void build_retrofit() throws Exception {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(mMockServer.url(""))
                .addConverterFactory(GsonConverterFactory.create())
                .build();

   }
}

My gradle.build

testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.1"
testCompile 'com.squareup.okhttp3:mockwebserver:3.3.0'

compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'

Which libraries version are you using?

Steve C
  • 1,034
  • 10
  • 12
  • I could run the test with no error `@RunWith(RobolectricGradleTestRunner.class) @Config(constants = BuildConfig.class, sdk = 23) public class ExampleUnitTest { private MockWebServer mMockServer = new MockWebServer(); @Test public void addition_isCorrect() throws Exception { Retrofit retrofit = new Retrofit.Builder() .baseUrl(mMockServer.url("")) .addConverterFactory(GsonConverterFactory.create()) .build(); } }` – Steve C Jun 28 '16 at 11:45