Robolectric + 改造 + 单元测试

Robolectric + Retrofit + Unit test

我有一个使用 Retrofit 2.0.2 映射 API 的项目。

它工作正常,所以我决定写一些功能测试以确保将来它没问题。出于多种原因,我希望他们 运行 脱离任何 Android 设备或​​模拟器。

问题是,我使用了一些 android classes(比如 Base64)所以我需要一个环境,这就是为什么我决定使用 Robolectric 3.0.

为了伪造我的 API(纯单元测试)的响应,我使用了 OkHttp 拦截器,几乎所有地方都有解释。

这就是问题所在,当我从 Android Studio (2.x) 运行 进行测试时,一切正常。但是当我从 Gradle 命令行 运行 它们时,似乎 Interceptor 没有拦截任何东西,我从 API.[=23= 得到了真实的响应]

单元测试代码如下:

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
public class TestService {

    private API api;

    @Rule
    public ErrorCollector collector = new ErrorCollector();

    @Before
    public void setUp() {
        // Initialize the API with fake data
        api = API.with("fake_client", "fake_secret", "fake_redirect");
    }

    @Test
    public void refreshToken() {
        Response<Token> tokenResponse = api.refreshToken("fake_refresh");
        collector.checkThat("no error", tokenResponse.error, nullValue());
        collector.checkThat("not null", tokenResponse.item, not(nullValue()));

        collector.checkThat("access token", tokenResponse.item.getAccessToken(), is("22fe0c13e995da4a44a63a7ff549badb5d337a42bf80f17424482e35d4cca91a"));
        collector.checkThat("expires at", tokenResponse.item.getExpiresAt(), is(1382962374L));
        collector.checkThat("expires in", tokenResponse.item.getExpiresIn(), is(3600L));
        collector.checkThat("refresh token", tokenResponse.item.getRefreshToken(), is("8eb667707535655f2d9e14fc6491a59f6e06f2e73170761259907d8de186b6a1"));
    }
}

API init 的代码(从 with 调用):

private API(@NonNull final String clientId, @NonNull final String clientSecret, @NonNull final String redirectUri) {
    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(new Interceptor() {
                @Override
                public okhttp3.Response intercept(Chain chain) throws IOException {
                    Request original = chain.request();
                    Request.Builder requestBuilder = original.newBuilder()
                            .header("Authorization", "Basic " + Base64.encodeToString((clientId + ":" + clientSecret).getBytes(), Base64.NO_WRAP).replace("\n", ""));
                    Request request = requestBuilder.build();
                    return chain.proceed(request);
                }
            })
            .addInterceptor(new APIInterceptor())
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Important point
            .build();
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(ROUTE_BASE)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
            .build();

    authService = retrofit.create(AuthService.class);
}

最后 API拦截器:

public class APIInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        ResponseBody response = ResponseBody.create(MediaType.parse("application/json"), response);
        return new Response.Builder()
            .request(request)
            .addHeader("Content-Type", "application/json")
            .protocol(Protocol.HTTP_1_1)
            .code(200)
            .body(response)
            .build();
    }
}

在我的测试中,但没有用。我该如何解决这个问题?

编辑

我有两种口味; class APIInterceptor 什么都不做的风味“制作”。还有一个风味“沙盒”,其中 class APIInterceptor 进行模拟。

所以当我 运行 从命令行进行测试时,我使用 gradlew :module:testSandboxDebugUnitTest

然后我尝试了一些有趣的东西。如果我获取 APIInterceptor class(“沙盒”)的内容,然后直接在 addInterceptor 中复制实现,那么它看起来像

API init 的代码(从 with 调用):

private API(@NonNull final String clientId, @NonNull final String clientSecret, @NonNull final String redirectUri) {
    OkHttpClient client = new OkHttpClient.Builder()
            .addInterceptor(new Interceptor() {
                @Override
                public okhttp3.Response intercept(Chain chain) throws IOException {
                    Request original = chain.request();
                    Request.Builder requestBuilder = original.newBuilder()
                            .header("Authorization", "Basic " + Base64.encodeToString((clientId + ":" + clientSecret).getBytes(), Base64.NO_WRAP).replace("\n", ""));
                    Request request = requestBuilder.build();
                    return chain.proceed(request);
                }
            })
            .addInterceptor(new Interceptor() {
                // The content of APIInterceptor here
            })
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Important point
            .build();
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(ROUTE_BASE)
            .client(client)
            .addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
            .build();

    authService = retrofit.create(AuthService.class);
}

这很好用!

所以问题似乎是 Robolectric 没有使用 flavors 中的实现,似乎 只是忽略了 flavors.

中的所有内容

好的,感谢 @EugenMartynov 我找到了答案。

事实上我正在使用另一个模块,这个模块总是有一个用于测试的拦截器,但它是相同的class名称在相同的包名称中。

所以当 gradle 编译时,它使用 发布模式 中的其他包的对象,它执行 网络操作 没有拦截任何东西!

解决方案是重命名 Interceptor class !