我想测试我的代码是否正确地从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的rv_fruits
方法加载到RecyclerView
enqueue()
。我怎样才能等待Retrofit回调,以便在数据加载到RecyclerView
之后检查断言?
答案 0 :(得分:1)
Robolectric尝试使所有执行同步,因为异步代码导致了不稳定的测试。如果您使用像AsynTask这样的标准内容,但是改装不能这样做,这样可以正常工作。
一种方法是隐藏一些在新线程中执行请求的Retrofit(或OkHttp)类,而不是直接执行它们。例如ShadowAsyncTask执行主线程中的所有runnable而不是使用新线程,原始实现将如何执行它。
另一种方法是与Espresso一起使用的IdlingResource概念。每当您启动一个请求时,就会在单个对象上递增一个计数器,并在请求完成时递减计数器。在测试中,您可以等到计数器为零。
简单的方法,但不好的做法将是一个简单的睡眠。
答案 1 :(得分:1)
我发现使用Robolectric和Retrofit(带回调)的主要问题。但是,当你是使用RestAdapter
设置RestAdapter.Builder
,您可以有条件地修改它(NB从不在您的实际应用中使用此配置):
if(singleThreaded){
Executor executor = Executors.newSingleThreadExecutor();
restAdapterBuilder.setExecutors(executor, executor);
}
通过为请求和回调添加此单线程执行程序,所有测试异步代码的标准方法(例如使用CountDownLatch
)都将起作用。
答案 2 :(得分: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();
}
};
}