6

我正在尝试使用 MockWebServer 和 Robolectric 对一些 API 调用进行单元测试。

我的测试类注释为:

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

但是,当尝试构建 Retrofit 实例时,出现以下异常:

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)

我用来构建改造实例的代码:

Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(mMockServer.url(""))
                .addConverterFactory(GsonConverterFactory.create())
                .build();

调用时返回上述异常.build()

我该如何解决这个问题?

4

4 回答 4

6

我有同样的问题。这里存在一个关于根本原因的错误,但在此过程中,我决定使用 Robolectric 3.0 和 dave-r12 概述的解决方案即创建我在下面包含的模拟。

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

}
于 2016-06-28T11:42:30.033 回答
1

似乎可行的替代解决方案是在设置 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();

它可能最终需要在改造方面进行纠正,以某种方式检测当前平台是否是 Robolectric 并返回一个虚拟执行器或其他东西。

于 2016-09-02T18:53:58.510 回答
1

如果您被困在 Robolectric 3 或更低版本并且您的目标是 API 25,那么正确的解决方案几乎与公认的解决方案相同。

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

唯一的区别在于isCleartextTrafficPermitted(String host)OkHttp 将尝试使用host参数调用此方法。

于 2017-02-16T14:03:07.573 回答
0

你能用 sdk=21 试试吗?我相信对 sdk=23 的支持仅是从 3.1-SNAPSHOT 添加的,而不是发布版本。

编辑:我错了,支持是从 3.1 版添加的,因为我可以成功运行测试

@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();

   }
}

我的 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'

您使用的是哪个库版本?

于 2016-06-28T07:09:33.413 回答