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 !
我有一个使用 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 !