Espresso:匹配对话框下的视图
Espresso: match a view under a dialog
我的测试用例相当简单:在 activity 主视图上,我有一个抽屉。此抽屉中的一个菜单项会打开一个对话框。我想断言,单击此菜单项,在打开对话框之前关闭抽屉。
这是我现在拥有的:
// Opens the drawer
onView(withId(R.id.activity_main_navigation_drawer)).perform(DrawerActions.open())
// Check that the drawer is opened
onView(withId(R.id.activity_main_navigation_drawer)).check(matches(isOpen()))
// Click on the menu item that closes the drawer and display the dialog
onView(withText(R.string.title_add_subscription)).perform(click())
// Check that the drawer is now closed: this won't work
onView(withId(R.id.activity_main_navigation_drawer)).check(matches(isClosed()))
请注意我还不想关闭对话框。因为目标是断言抽屉已关闭 before/during 对话框的显示。
问题是最后一条指令会上升 NoMatchingViewException
,因为抽屉似乎不再位于视图层次结构中。看起来显示对话框使它成为视图层次结构的新根。不过,我知道 activity 视图存在于某处。我想知道,在这种情况下,如何在显示对话框时匹配对话框下的vie。
这是一个非常好的问题,令我惊讶的是它以前在其他任何地方都没有asked/discussed。
您的推理是正确的,当对话框出现时,视图层次结构发生变化,因此 onView(withId(R.id.activity_main_navigation_drawer))
给出 NoMatchingViewException。在打开对话框之前将其分配给 ViewInteraction
对象也无济于事,因为 Espresso 将尝试使用新视图层次结构匹配对象,即使您想使用旧对象(使用旧视图层次结构匹配) )
我建议的解决方案应该适合您,据我所知,这是实现您想要的目标的唯一方法。这有点违背 UI 测试的精神,因为我们只想模仿用户的操作并尝试以用户看到系统的方式断言事物,但我认为这个解决方案仍然可以,因为我们没有与系统交互使用我们的系统代码。 (我们只用它来断言)
Activity activity = getActivityInstance();
DrawerLayout drawerLayout=(DrawerLayout) activity.findViewById((R.id.drawerlayout));
// your test code
assertFalse(drawerLayout.isDrawerOpen(GravityCompat.START))
// your remaining test code
getActivityInstance
的方法(有超过 999999 种不同的方法来获取 activity 实例。这个取自
private Activity getActivityInstance(){
final Activity[] currentActivity = {null};
getInstrumentation().runOnMainSync(new Runnable(){
public void run(){
Collection<Activity> resumedActivity = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED);
Iterator<Activity> it = resumedActivity.iterator();
currentActivity[0] = it.next();
}
});
return currentActivity[0];
}
作为个人喜好,我不喜欢使用activity的实例,除非它是为了测试一些非常重要的东西而我真的无法通过其他方式测试它。
我的测试用例相当简单:在 activity 主视图上,我有一个抽屉。此抽屉中的一个菜单项会打开一个对话框。我想断言,单击此菜单项,在打开对话框之前关闭抽屉。
这是我现在拥有的:
// Opens the drawer
onView(withId(R.id.activity_main_navigation_drawer)).perform(DrawerActions.open())
// Check that the drawer is opened
onView(withId(R.id.activity_main_navigation_drawer)).check(matches(isOpen()))
// Click on the menu item that closes the drawer and display the dialog
onView(withText(R.string.title_add_subscription)).perform(click())
// Check that the drawer is now closed: this won't work
onView(withId(R.id.activity_main_navigation_drawer)).check(matches(isClosed()))
请注意我还不想关闭对话框。因为目标是断言抽屉已关闭 before/during 对话框的显示。
问题是最后一条指令会上升 NoMatchingViewException
,因为抽屉似乎不再位于视图层次结构中。看起来显示对话框使它成为视图层次结构的新根。不过,我知道 activity 视图存在于某处。我想知道,在这种情况下,如何在显示对话框时匹配对话框下的vie。
这是一个非常好的问题,令我惊讶的是它以前在其他任何地方都没有asked/discussed。
您的推理是正确的,当对话框出现时,视图层次结构发生变化,因此 onView(withId(R.id.activity_main_navigation_drawer))
给出 NoMatchingViewException。在打开对话框之前将其分配给 ViewInteraction
对象也无济于事,因为 Espresso 将尝试使用新视图层次结构匹配对象,即使您想使用旧对象(使用旧视图层次结构匹配) )
我建议的解决方案应该适合您,据我所知,这是实现您想要的目标的唯一方法。这有点违背 UI 测试的精神,因为我们只想模仿用户的操作并尝试以用户看到系统的方式断言事物,但我认为这个解决方案仍然可以,因为我们没有与系统交互使用我们的系统代码。 (我们只用它来断言)
Activity activity = getActivityInstance();
DrawerLayout drawerLayout=(DrawerLayout) activity.findViewById((R.id.drawerlayout));
// your test code
assertFalse(drawerLayout.isDrawerOpen(GravityCompat.START))
// your remaining test code
getActivityInstance
的方法(有超过 999999 种不同的方法来获取 activity 实例。这个取自
private Activity getActivityInstance(){
final Activity[] currentActivity = {null};
getInstrumentation().runOnMainSync(new Runnable(){
public void run(){
Collection<Activity> resumedActivity = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED);
Iterator<Activity> it = resumedActivity.iterator();
currentActivity[0] = it.next();
}
});
return currentActivity[0];
}
作为个人喜好,我不喜欢使用activity的实例,除非它是为了测试一些非常重要的东西而我真的无法通过其他方式测试它。