Robolectric + Retrofit +单元测试

时间:2016-06-18 08:07:24

标签: android unit-testing robolectric retrofit2

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

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

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

为了伪造我的API(纯单元测试)的响应,我使用 OkHttp Interceptor ,几乎无处不在。

问题是,当我从Android Studio(2.x)运行我的测试时,一切正常。 但是,当我从gradle 命令行运行它们时,似乎Interceptor没有拦截任何内容,我从我的API获得了真实的响应。

以下是单元测试的代码:

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

最后是 APIInterceptor

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

在我的测试中,但它没有用。

如果你们有任何想法:)

谢谢,

编辑27/06/2016:

仅供参考,我有2种口味生产,其中班级APIInterceptor什么都不做。还有一个风味沙箱,其中类APIInterceptor执行模拟。

因此,当我从命令行运行测试时,我使用gradlew :module:testSandboxDebugUnitTest

然后我尝试了一些有趣的事情

如果我获取APIInterceptor类(沙箱一个)的内容,然后直接在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 使用各种风格的实现,似乎只是忽略了各种风格 ..

1 个答案:

答案 0 :(得分:2)

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

实际上我正在使用另一个模块,而且这个模块总是有一个拦截器用于测试,但它在相同的包名中是相同的类名

因此,当gradle正在编译时,它正在使用发布模式中的其他包中的对象执行网络操作而不拦截任何内容!

解决方案是重命名 Interceptor 类!