Android 使用 Retrofit2 和 Mockito 进行单元测试或 Robolectric
Android Unit Test with Retrofit2 and Mockito or Robolectric
我可以测试 retrofit2beta4 的真实响应吗?我需要 Mockito 还是 Robolectic?
我的项目中没有活动,它将是一个库,我需要测试服务器是否正确响应。
现在我有了这样的代码并卡住了...
@Mock
ApiManager apiManager;
@Captor
private ArgumentCaptor<ApiCallback<Void>> cb;
@Before
public void setUp() throws Exception {
apiManager = ApiManager.getInstance();
MockitoAnnotations.initMocks(this);
}
@Test
public void test_login() {
Mockito.verify(apiManager)
.loginUser(Mockito.eq(login), Mockito.eq(pass), cb.capture());
// cb.getValue();
// assertEquals(cb.getValue().isError(), false);
}
我可以做出假的反应,但我需要测试真实的。是成功吗? body 正确吗?
你能帮我写代码吗?
测试真实的服务器请求通常不是一个好主意。有关该主题的有趣讨论,请参阅 this blog post。根据作者的说法,使用您的真实服务器是一个问题,因为:
- Another moving piece that can intermittently fail
- Requires some expertise outside of the Android domain to deploy the server and keep it updated
- Difficult to trigger error/edge cases
- Slow test execution (still making HTTP calls)
您可以通过使用模拟服务器(例如 OkHttp 的 MockWebServer)模拟真实响应结果来避免上述所有问题。例如:
@Test
public void test() throws IOException {
MockWebServer mockWebServer = new MockWebServer();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(mockWebServer.url("").toString())
//TODO Add your Retrofit parameters here
.build();
//Set a response for retrofit to handle. You can copy a sample
//response from your server to simulate a correct result or an error.
//MockResponse can also be customized with different parameters
//to match your test needs
mockWebServer.enqueue(new MockResponse().setBody("your json body"));
YourRetrofitService service = retrofit.create(YourRetrofitService.class);
//With your service created you can now call its method that should
//consume the MockResponse above. You can then use the desired
//assertion to check if the result is as expected. For example:
Call<YourObject> call = service.getYourObject();
assertTrue(call.execute() != null);
//Finish web server
mockWebServer.shutdown();
}
如果您需要模拟网络延迟,您可以自定义响应如下:
MockResponse response = new MockResponse()
.addHeader("Content-Type", "application/json; charset=utf-8")
.addHeader("Cache-Control", "no-cache")
.setBody("{}");
response.throttleBody(1024, 1, TimeUnit.SECONDS);
或者,您可以使用 MockRetrofit
和 NetworkBehavior
来模拟 API 响应。请参阅 here 如何使用它的示例。
最后,如果您只想测试 Retrofit 服务,最简单的方法是创建它的模拟版本,为您的测试发出模拟结果。例如,如果您有以下 GitHub
服务接口:
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
然后您可以为您的测试创建以下 MockGitHub
:
public class MockGitHub implements GitHub {
private final BehaviorDelegate<GitHub> delegate;
private final Map<String, Map<String, List<Contributor>>> ownerRepoContributors;
public MockGitHub(BehaviorDelegate<GitHub> delegate) {
this.delegate = delegate;
ownerRepoContributors = new LinkedHashMap<>();
// Seed some mock data.
addContributor("square", "retrofit", "John Doe", 12);
addContributor("square", "retrofit", "Bob Smith", 2);
addContributor("square", "retrofit", "Big Bird", 40);
addContributor("square", "picasso", "Proposition Joe", 39);
addContributor("square", "picasso", "Keiser Soze", 152);
}
@Override public Call<List<Contributor>> contributors(String owner, String repo) {
List<Contributor> response = Collections.emptyList();
Map<String, List<Contributor>> repoContributors = ownerRepoContributors.get(owner);
if (repoContributors != null) {
List<Contributor> contributors = repoContributors.get(repo);
if (contributors != null) {
response = contributors;
}
}
return delegate.returningResponse(response).contributors(owner, repo);
}
}
然后您可以在测试中使用 MockGitHub
来模拟您正在寻找的响应类型。有关完整示例,请参阅 SimpleService and SimpleMockService for this Retrofit example.
的实现
说了这么多,如果你绝对必须连接到实际的服务器,你可以将 Retrofit 设置为与自定义同步工作 ImmediateExecutor
:
public class ImmediateExecutor implements Executor {
@Override public void execute(Runnable command) {
command.run();
}
}
然后将它应用到您在构建 Retrofit 时使用的OkHttpClient
:
OkHttpClient client = OkHttpClient.Builder()
.dispatcher(new Dispatcher(new ImmediateExecutor()))
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(client)
//Your params
.build();
答案比我预期的简单:
使用 CountDownLatch 让您的测试等到您调用 countDown()
public class SimpleRetrofitTest {
private static final String login = "your@login";
private static final String pass = "pass";
private final CountDownLatch latch = new CountDownLatch(1);
private ApiManager apiManager;
private OAuthToken oAuthToken;
@Before
public void beforeTest() {
apiManager = ApiManager.getInstance();
}
@Test
public void test_login() throws InterruptedException {
Assert.assertNotNull(apiManager);
apiManager.loginUser(login, pass, new ApiCallback<OAuthToken>() {
@Override
public void onSuccess(OAuthToken token) {
oAuthToken = token;
latch.countDown();
}
@Override
public void onFailure(@ResultCode.Code int errorCode, String errorMessage) {
latch.countDown();
}
});
latch.await();
Assert.assertNotNull(oAuthToken);
}
@After
public void afterTest() {
oAuthToken = null;
}}
除非您正在测试 QA 服务器 API,否则这不是一个好主意,原因有几个。
- 首先,您要使用 bad/fake 填充您的生产数据库
数据
- 使用服务器资源,什么时候可以更好地用于服务
有效请求
使用 Mockito 或模拟您的回复的最佳方式
此外,如果您必须测试您的产品 API,请测试一次并添加 @Ignore 注释。这样他们就不会一直 运行 并且不会用虚假数据向您的服务器发送垃圾邮件,并且只要您觉得 api 行为不正确就可以使用它。
我可以测试 retrofit2beta4 的真实响应吗?我需要 Mockito 还是 Robolectic?
我的项目中没有活动,它将是一个库,我需要测试服务器是否正确响应。 现在我有了这样的代码并卡住了...
@Mock
ApiManager apiManager;
@Captor
private ArgumentCaptor<ApiCallback<Void>> cb;
@Before
public void setUp() throws Exception {
apiManager = ApiManager.getInstance();
MockitoAnnotations.initMocks(this);
}
@Test
public void test_login() {
Mockito.verify(apiManager)
.loginUser(Mockito.eq(login), Mockito.eq(pass), cb.capture());
// cb.getValue();
// assertEquals(cb.getValue().isError(), false);
}
我可以做出假的反应,但我需要测试真实的。是成功吗? body 正确吗? 你能帮我写代码吗?
测试真实的服务器请求通常不是一个好主意。有关该主题的有趣讨论,请参阅 this blog post。根据作者的说法,使用您的真实服务器是一个问题,因为:
- Another moving piece that can intermittently fail
- Requires some expertise outside of the Android domain to deploy the server and keep it updated
- Difficult to trigger error/edge cases
- Slow test execution (still making HTTP calls)
您可以通过使用模拟服务器(例如 OkHttp 的 MockWebServer)模拟真实响应结果来避免上述所有问题。例如:
@Test
public void test() throws IOException {
MockWebServer mockWebServer = new MockWebServer();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(mockWebServer.url("").toString())
//TODO Add your Retrofit parameters here
.build();
//Set a response for retrofit to handle. You can copy a sample
//response from your server to simulate a correct result or an error.
//MockResponse can also be customized with different parameters
//to match your test needs
mockWebServer.enqueue(new MockResponse().setBody("your json body"));
YourRetrofitService service = retrofit.create(YourRetrofitService.class);
//With your service created you can now call its method that should
//consume the MockResponse above. You can then use the desired
//assertion to check if the result is as expected. For example:
Call<YourObject> call = service.getYourObject();
assertTrue(call.execute() != null);
//Finish web server
mockWebServer.shutdown();
}
如果您需要模拟网络延迟,您可以自定义响应如下:
MockResponse response = new MockResponse()
.addHeader("Content-Type", "application/json; charset=utf-8")
.addHeader("Cache-Control", "no-cache")
.setBody("{}");
response.throttleBody(1024, 1, TimeUnit.SECONDS);
或者,您可以使用 MockRetrofit
和 NetworkBehavior
来模拟 API 响应。请参阅 here 如何使用它的示例。
最后,如果您只想测试 Retrofit 服务,最简单的方法是创建它的模拟版本,为您的测试发出模拟结果。例如,如果您有以下 GitHub
服务接口:
public interface GitHub {
@GET("/repos/{owner}/{repo}/contributors")
Call<List<Contributor>> contributors(
@Path("owner") String owner,
@Path("repo") String repo);
}
然后您可以为您的测试创建以下 MockGitHub
:
public class MockGitHub implements GitHub {
private final BehaviorDelegate<GitHub> delegate;
private final Map<String, Map<String, List<Contributor>>> ownerRepoContributors;
public MockGitHub(BehaviorDelegate<GitHub> delegate) {
this.delegate = delegate;
ownerRepoContributors = new LinkedHashMap<>();
// Seed some mock data.
addContributor("square", "retrofit", "John Doe", 12);
addContributor("square", "retrofit", "Bob Smith", 2);
addContributor("square", "retrofit", "Big Bird", 40);
addContributor("square", "picasso", "Proposition Joe", 39);
addContributor("square", "picasso", "Keiser Soze", 152);
}
@Override public Call<List<Contributor>> contributors(String owner, String repo) {
List<Contributor> response = Collections.emptyList();
Map<String, List<Contributor>> repoContributors = ownerRepoContributors.get(owner);
if (repoContributors != null) {
List<Contributor> contributors = repoContributors.get(repo);
if (contributors != null) {
response = contributors;
}
}
return delegate.returningResponse(response).contributors(owner, repo);
}
}
然后您可以在测试中使用 MockGitHub
来模拟您正在寻找的响应类型。有关完整示例,请参阅 SimpleService and SimpleMockService for this Retrofit example.
说了这么多,如果你绝对必须连接到实际的服务器,你可以将 Retrofit 设置为与自定义同步工作 ImmediateExecutor
:
public class ImmediateExecutor implements Executor {
@Override public void execute(Runnable command) {
command.run();
}
}
然后将它应用到您在构建 Retrofit 时使用的OkHttpClient
:
OkHttpClient client = OkHttpClient.Builder()
.dispatcher(new Dispatcher(new ImmediateExecutor()))
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(client)
//Your params
.build();
答案比我预期的简单:
使用 CountDownLatch 让您的测试等到您调用 countDown()
public class SimpleRetrofitTest {
private static final String login = "your@login";
private static final String pass = "pass";
private final CountDownLatch latch = new CountDownLatch(1);
private ApiManager apiManager;
private OAuthToken oAuthToken;
@Before
public void beforeTest() {
apiManager = ApiManager.getInstance();
}
@Test
public void test_login() throws InterruptedException {
Assert.assertNotNull(apiManager);
apiManager.loginUser(login, pass, new ApiCallback<OAuthToken>() {
@Override
public void onSuccess(OAuthToken token) {
oAuthToken = token;
latch.countDown();
}
@Override
public void onFailure(@ResultCode.Code int errorCode, String errorMessage) {
latch.countDown();
}
});
latch.await();
Assert.assertNotNull(oAuthToken);
}
@After
public void afterTest() {
oAuthToken = null;
}}
除非您正在测试 QA 服务器 API,否则这不是一个好主意,原因有几个。
- 首先,您要使用 bad/fake 填充您的生产数据库 数据
- 使用服务器资源,什么时候可以更好地用于服务 有效请求
使用 Mockito 或模拟您的回复的最佳方式
此外,如果您必须测试您的产品 API,请测试一次并添加 @Ignore 注释。这样他们就不会一直 运行 并且不会用虚假数据向您的服务器发送垃圾邮件,并且只要您觉得 api 行为不正确就可以使用它。