【问题标题】:Robolectric + Retrofit + Unit testRobolectric + 改造 + 单元测试
【发布时间】:2016-10-20 01:41:44
【问题描述】:

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

它工作正常,所以我决定编写一些功能测试以确保它在未来可以正常工作。出于多种原因,我希望他们用完任何 Android 设备或模拟器。

问题是,我使用了一些 android 类(如 Base64),所以我需要一个环境,这就是我决定使用 Robolectric 3.0 的原因。

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

这是问题所在,当我从 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();
    }
}

在我的测试中,但它不起作用。我该如何解决这个问题?

编辑

我有两种口味; 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 没有使用风味中的实现,它似乎只是忽略了风味中的所有内容

【问题讨论】:

  • shadows-httpclient 仅适用于 Apache HTTP 客户端。你正在使用 OkHttp。其次,它不是单元测试,而是集成测试。那么你想测试什么?
  • @EugenMartynov 我的错,这些是集成测试,我只是想伪造 API 的返回以检查一切是否正常(从接收 http 响应到返回填充的 java 对象)问题是甚至APIInterceptor 似乎也被绕过了。
  • 现在很清楚了。因为我以为你只是想测试拦截器,但现在你澄清了你想测试整个 api 调用
  • 我会在 AS 中调试测试并在运行 gradle 时放置更多日志。找出有什么区别
  • 我认为这不是 Robolectric 的东西。库模块中的风格可能是个问题,尤其是您应该知道库模块总是与发布构建类型相关联

标签: android unit-testing robolectric retrofit2


【解决方案1】:

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

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

所以当gradle 编译时,它在发布模式 中使用另一个包中的对象,它执行网络操作 而不拦截任何东西!

解决方案是重命名 Interceptor 类!

【讨论】:

    猜你喜欢
    • 2014-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-20
    相关资源
    最近更新 更多