Fragment中RecyclerView单元测试报NoMatchingRootException如何解决
错误根因分析
- Fragment事务提交是异步操作:你调用
openFragment时使用commit()提交事务,该操作是异步的,setUp方法执行完后Fragment还没完成挂载和布局渲染,控件未就绪。 - 多余的
inRoot匹配逻辑:你强行指定DecorView匹配规则,和当前测试环境中实际获得焦点的窗口DecorView不匹配,从错误日志可见当前检测到的Root窗口has-window-focus=false,导致匹配失败。 - 代码逻辑错误:测试类中声明的Fragment变量类型错误,你创建的是
ChatListFragment,却声明为ListTestFragment类型,会导致类型转换异常。
解决方案
- 修正类型错误
把测试类中的成员变量声明修改为正确类型:// 错误写法 // private ListTestFragment fragment; // 正确写法 private ChatListFragment fragment; - 移除多余的
inRoot匹配逻辑
Espresso默认会匹配当前应用的顶层窗口,不需要手动指定DecorView匹配规则,删除所有测试方法中的inRoot相关代码即可。 - 确保Fragment事务同步完成
两种可选方案:- 修改
openFragment工具类的提交方式,将commit()改为commitNow()(当前场景没有加回退栈,支持同步提交):
// 错误写法 // fragmentTransaction.commit(); // 正确写法 fragmentTransaction.commitNow();- 如果你不想修改原有业务代码,可以在
setUp中增加等待事务完成的逻辑:
@Before public void setUp() { activityActivityTestRule.getActivity().runOnUiThread(() -> { activityActivityTestRule.getActivity().openChatFragment(null); }); // 也可以用IdlingResource实现更优雅的等待逻辑 Thread.sleep(1000); fragment = activityActivityTestRule.getActivity().chatFragment; } - 修改
- 修正RecyclerView计数逻辑
测试滚动时不要硬编码20的数量,从Adapter中获取真实的条目数:@Test public void testCaseForRecyclerScroll() { RecyclerView recyclerView = activityActivityTestRule.getActivity().findViewById(R.id.recyclerview); int itemCount = recyclerView.getAdapter().getItemCount(); Espresso.onView(ViewMatchers.withId(R.id.recyclerview)) .perform(RecyclerViewActions.scrollToPosition(itemCount - 1)); } - 额外注意:如果你的RecyclerView数据是异步请求加载的,需要添加
IdlingResource等待数据加载完成后再执行操作,避免出现条目未加载的异常。
内容的提问来源于stack exchange,提问作者Nitish
相关产品推荐
相关产品推荐

