Espresso抛出NoMatchingViewException但视图存在于视图层级的问题排查
以下是针对该问题的具体排查方向:
1. 验证withParent的层级匹配逻辑
虽然视图层级显示目标TextView是user_feedback的直接子View,但Espresso的withParent严格匹配直接父视图,你可以尝试替换为withAncestor(匹配任意祖先视图)来测试,排除层级判定的问题:
onView( allOf( withId(R.id.message), withText(R.string.feedback_pin_blocked), withAncestor(withId(R.id.user_feedback)) ) ).check(matches(ViewMatchers.isDisplayed()))
2. 排查字符串匹配的细节问题
withText(R.string.feedback_pin_blocked)通过资源ID匹配字符串,可能存在以下隐藏问题:
- 本地化差异:测试设备的语言区域与资源文件默认区域不一致,导致实际文本和资源文本不匹配
- 不可见字符:字符串包含空格、换行、全角/半角差异等肉眼难以察觉的字符
可以先尝试用字符串字面量替代资源ID测试,确认是否是字符串匹配导致的问题:
onView( allOf( withId(R.id.message), withText("Your PIN has been blocked."), withParent(withId(R.id.user_feedback)) ) ).check(matches(ViewMatchers.isDisplayed()))
3. 检查isDisplayed()的判定条件
isDisplayed()要求视图同时满足:自身visibility=VISIBLE、所有父视图可见、视图尺寸大于0且部分/完全在屏幕内。你可以先去掉isDisplayed()的检查,仅验证视图是否存在:
onView( allOf( withId(R.id.message), withText(R.string.feedback_pin_blocked), withParent(withId(R.id.user_feedback)) ) ).check(matches(ViewMatchers.exists()))
如果exists()成功,再进一步排查视图是否被裁剪或超出屏幕范围,可使用isCompletelyDisplayed()替代isDisplayed()测试。
4. 排查重复ID的可能性
即使当前视图层级只显示了一个目标视图,布局中可能存在多个相同ID的视图(比如多Fragment、ViewPager页面中),导致Espresso无法定位到正确的那个。可以通过inRoot匹配器指定根视图缩小范围:
onView( allOf( withId(R.id.message), withText(R.string.feedback_pin_blocked), withParent(withId(R.id.user_feedback)) ) ).inRoot(withId(R.id.activity_root_layout)) // 替换为你的根布局ID .check(matches(ViewMatchers.isDisplayed()))
5. 拆分匹配器逐步排查
将组合的匹配器拆分为单个条件,逐个验证,定位到具体哪个条件导致匹配失败:
- 先验证ID匹配:
onView(withId(R.id.message)).check(matches(ViewMatchers.exists())); - 再添加文本匹配:
onView(allOf(withId(R.id.message), withText(R.string.feedback_pin_blocked))).check(matches(ViewMatchers.exists())); - 最后添加父视图匹配:
onView(allOf(withId(R.id.message), withText(R.string.feedback_pin_blocked), withParent(withId(R.id.user_feedback)))).check(matches(ViewMatchers.exists()));
问题背景
匹配器代码:
onView( allOf( withId(R.id.message), withText(R.string.feedback_pin_blocked), withParent(withId(R.id.user_feedback)) ) ).check(matches(ViewMatchers.isDisplayed()))
异常信息:
androidx.test.espresso.NoMatchingViewException: No views in hierarchy found matching: (view.getId() is <2131296583/**************:id/message> and an instance of android.widget.TextView and view.getText() equals string from resource id: <2131820672> [feedback_pin_blocked] value: Your PIN has been blocked. and view.getParent() view.getId() is <2131296829>)
视图层级(相关部分):
+-------LinearLayout{id=2131296829, res-name=user_feedback, visibility=VISIBLE, width=1080, height=155, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, layout-params=android.widget.LinearLayout$LayoutParams@YYYYYY, tag=null, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=182.0, child-count=3}
|
+--------AppCompatTextView{id=2131296583, res-name=message, visibility=VISIBLE, width=611, height=67, has-focus=false, has-focusable=false, has-window-focus=true, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, layout-params=android.widget.LinearLayout$LayoutParams@YYYYYY, tag=null, root-is-layout-requested=false, has-input-connection=false, x=44.0, y=44.0, text=Your PIN has been blocked., input-type=0, ime-target=false, has-links=false}
内容的提问来源于stack exchange,提问作者Marcel

