简体   繁体   English

Robolectric + 改造 + 单元测试

[英]Robolectric + Retrofit + Unit test

I have a project that maps an API using Retrofit 2.0.2.我有一个使用 Retrofit 2.0.2 映射 API 的项目。

It works fine, so I decided to write some functional tests to make sure it's ok in the future.它工作正常,所以我决定编写一些功能测试以确保将来它没问题。 For multiple reasons, I want them to run out of any Android device or emulator.出于多种原因,我希望它们能用完任何 Android 设备或模拟器。

The thing is, I use some android classes (like Base64) so I needed an env, that's why I decided to use Robolectric 3.0.问题是,我使用了一些 android 类(如 Base64),所以我需要一个 env,这就是我决定使用 Robolectric 3.0 的原因。

In order to fake the responses of my API (pure unit test) I use an OkHttp Interceptor as explained almost everywhere.为了伪造我的 API 的响应(纯单元测试),我几乎到处都使用了 OkHttp 拦截器。

Here is the problem, when I run my tests from Android Studio (2.x) everything works fine.问题是,当我从 Android Studio (2.x) 运行测试时,一切正常。 But when I run them from a Gradle command line, it seems the Interceptor doesn't intercept anything and I get a real response from my API.但是当我从 Gradle 命令行运行它们时,似乎Interceptor器没有拦截任何东西,我从我的 API 得到了真正的响应。

Here is the code of the Unit test:下面是单元测试的代码:

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

The code of the API init (called from with ): 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);
}

And finally the APIInterceptor :最后是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();
    }
}

In my test but it didn't work.在我的测试中,但它没有用。 How can I fix this?我该如何解决这个问题?

Edit编辑

I have 2 flavors;我有 2 种口味; a flavor "production" where the class APIInterceptor does nothing.APIInterceptor什么都不做的风味“生产”。 And a flavor "sandbox" where the class APIInterceptor does the mock.还有一个风味“沙箱”,类APIInterceptor进行模拟。

So when I run the tests from command line I use gradlew :module:testSandboxDebugUnitTest因此,当我从命令行运行测试时,我使用gradlew :module:testSandboxDebugUnitTest

Then I tried something interesting.然后我尝试了一些有趣的事情。 If I take the content of the APIInterceptor class (the "sandbox" one) and then copy the implementation directly in the addInterceptor so it looks like如果我获取APIInterceptor类(“沙盒”类)的内容,然后直接在addInterceptor复制实现,则它看起来像

The code of the API init (called from with ): 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);
}

This works fine!这工作正常!

So the issue seems to be that Robolectric does not use the implementations in flavors, it seems it just ignores everything in flavors .所以问题似乎是 Robolectric 没有使用 flavors 中的实现,它似乎只是忽略了 flavors 中的所有内容

Ok, thanks to @EugenMartynov I found the answer.好的,感谢@EugenMartynov我找到了答案。

In fact I was using another module and this module has always an interceptor for tests, but it was the same class name in the same package name .事实上,我正在使用另一个模块,这个模块总是有一个用于测试的拦截器,但它是同一个包名中的同一个类名

So when gradle was compiling, it was using the object from the other package in release mode which does the network operations without intercepting anything !因此,当gradle编译时,它在发布模式下使用另一个包中的对象,该对象在不拦截任何内容的情况执行网络操作

The solution was to rename the Interceptor class !解决方案是重命名拦截器类!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM