【问题标题】:Robolectric and Retrofit - wait for responseRobolectric 和改造 - 等待响应
【发布时间】:2016-08-02 02:19:23
【问题描述】:

我想测试我的代码是否正确地从 API 下载数据(使用 Retrofit)并将它们显示在 RecyclerView 中。为此,我创建了一个模拟 API 的拦截器(基于 this solution)并创建了一个测试(使用 Robolectric):

    @Test
    public void listLoadedCorrectlyTest() {
        MyListFragment myListFragment = new MyListFragment();
        Bundle args = new Bundle();
        FragmentTestUtil.startFragment(myListFragment);
        Robolectric.flushForegroundThreadScheduler();
        JSONArray responseArray = new JSONArray();
        try {
            responseArray = new JSONArray(ApiInterceptor.MOCK_RESPONSE);
        } catch (JSONException e) {
            Log.e("JSON", e.getMessage());
        }
        int adapterItemCount = ((RecyclerView) myListFragment.getView()
                .findViewById(R.id.rv_fruits)).getAdapter().getItemCount();
        assertThat(adapterItemCount).isEqualTo(responseArray.length());
    }

问题是测试有时通过,有时失败。我已经调试了代码,我知道这是因为 MyListFragment 中的数据是使用 Retrofit 的 enqueue() 方法加载到 rv_fruits RecyclerView 的。如何等待 Retrofit 回调,以便在将数据加载到 RecyclerView 后检查断言?

【问题讨论】:

  • 你能显示你的片段代码吗?你可以在那里模拟网络吗?

标签: android retrofit robolectric android-testing retrofit2


【解决方案1】:

另一种方法:

我一直在使用Awaitility 工具并取得了巨大成功。它本质上与闩锁相同,但更具可读性。

在你的情况下:

@Test
public void listLoadedCorrectlyTest() {
    MyListFragment myListFragment = new MyListFragment();
    Bundle args = new Bundle();
    FragmentTestUtil.startFragment(myListFragment);
    Robolectric.flushForegroundThreadScheduler();
    JSONArray responseArray = new JSONArray();
    try {
        responseArray = new JSONArray(ApiInterceptor.MOCK_RESPONSE);
    } catch (JSONException e) {
        Log.e("JSON", e.getMessage());
    }
    int adapterItemCount = ((RecyclerView) myListFragment.getView()
            .findViewById(R.id.rv_fruits)).getAdapter().getItemCount();
    await().atMost(15, TimeUnit.SECONDS)
                      .until(responseIsSameAsCount(myListFragment, responseArray)
}


    private Callable<Boolean> responseIsSameAsCount(View myListFragment, JSONArray responseArray) {
    return new Callable<Boolean>() {
        @Override
        public Boolean call() throws Exception {
            return  ((RecyclerView) myListFragment.getView()
            .findViewById(R.id.rv_fruits)).getAdapter().getItemCount() == responseArray.length();
        }
    };
}

【讨论】:

    【解决方案2】:

    Robolectric 尝试使所有执行同步,因为异步代码会导致不稳定的测试。如果您使用像 AsynTask 这样的标准东西但改造不这样做,这很好用。

    一种方法是隐藏一些 Retrofit(或 OkHttp)类,这些类在新线程中执行请求,而不是直接执行它们。例如ShadowAsyncTask 在主线程中执行所有可运行的对象,而不是使用新线程来执行原始实现。

    另一种方法是与 Espresso 一起使用的 IdlingResource 概念。每当您启动请求时,然后在单例对象上增加一个计数器,并在请求完成时减少计数器。在您的测试中,您可以等到计数器为零。

    简单的方法但不好的做法是简单的睡眠。

    【讨论】:

    • 您认为在这种情况下使用“CountDownLatch”是个好主意吗?它可以与 Robolectric 一起使用吗?
    • 基本上 CountDownLatch 将与 Robolectric 一起使用。一个缺点是您必须指定 countDown() 调用的预期计数。这可能足以满足您的需求! CountDownLatch 没有 countUp() 方法,如果您不知道请求的计数,这可能是必需的。如果你需要 countUp() 然后试试stackoverflow.com/a/14260716/3619179
    【解决方案3】:

    我发现将 Robolectric 与 Retrofit(带有回调)一起使用时存在重大问题。但是,当你是例如设置RestAdapterRestAdapter.Builder 你可以有条件地修改它(注意不要在你的实际应用程序中使用这个配置):

    if(singleThreaded){
        Executor executor = Executors.newSingleThreadExecutor();
        restAdapterBuilder.setExecutors(executor, executor);
    }
    

    通过为请求和回调添加这个单线程执行器,所有测试异步代码的标准方法(例如使用CountDownLatch)都将起作用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-22
      • 1970-01-01
      • 1970-01-01
      • 2021-08-02
      • 1970-01-01
      • 2020-02-25
      • 2017-07-30
      • 2017-01-03
      相关资源
      最近更新 更多