使用 Retrofit2 和 Mockito 或 Robolectric 进行 Android 单元测试

新手上路,请多包涵

我可以测试 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);
}

我可以做出假的反应,但我需要测试真实的。是成功吗?它的身体是正确的吗?你能帮我写代码吗?

原文由 AndrewS 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 850
2 个回答

答案比我预期的要简单:

使用 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;
}}

原文由 AndrewS 发布,翻译遵循 CC BY-SA 3.0 许可协议

测试真实的服务器请求通常不是一个好主意。有关该主题的有趣讨论,请参阅 此博客文章。根据作者的说法,使用您的真实服务器是一个问题,因为:

  • 另一个可能间歇性失败的移动部件
  • 需要 Android 领域以外的一些专业知识来部署服务器并保持更新
  • 难以触发错误/边缘情况
  • 测试执行缓慢(仍在进行 HTTP 调用)

您可以通过使用模拟服务器(例如 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);

或者,您可以使用 MockRetrofitNetworkBehavior 来模拟 API 响应。请 在此处 查看如何使用它的示例。

最后,如果您只想测试 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 来模拟您正在寻找的响应类型。有关完整示例,请参阅此 Retrofit 示例SimpleServiceSimpleMockService 的实现。

说了这么多,如果你绝对必须连接到实际的服务器,你可以将 Retrofit 设置为与自定义同步工作 ImmediateExecutor

 public class ImmediateExecutor implements Executor {
    @Override public void execute(Runnable command) {
        command.run();
    }
}

然后将它应用到 OkHttpClient 你在构建 Retrofit 时使用:

 OkHttpClient client = OkHttpClient.Builder()
        .dispatcher(new Dispatcher(new ImmediateExecutor()))
        .build();

Retrofit retrofit = new Retrofit.Builder()
        .client(client)
        //Your params
        .build();

原文由 Ricardo 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题