使用Espresso检查对话框是否显示

102
我正在尝试使用新的android-test-kit (Espresso)编写一些测试。但我找不到任何有关如何检查对话框是否显示并执行某些操作(例如单击积极和消极按钮等)的信息。请注意,对话框可能也是由WebView而非应用程序本身显示的。

如能提供任何帮助,将不胜感激。我只需要一个链接或一些基础示例代码来:

  1. 检查对话框是否出现
  2. 对对话框上的按钮执行点击操作
  3. 与对话框的内部视图进行交互(如果它是自定义视图)
  4. 在对话框外执行点击操作,并检查它是否正在显示(例如,如果在对话框生成器上调用了setCancelable(false),我们想要检查它是否正在显示)

非常感谢!


下面是我的答案,有任何反馈吗? - denys
7个回答

145
  • 要验证对话框是否出现,您只需检查是否显示具有存在于对话框中的文本的视图:

  • onView(withText("dialogText")).check(matches(isDisplayed()));
    

    或者,基于带有id的文本

    onView(withId(R.id.myDialogTextId)).check(matches(allOf(withText(myDialogText), isDisplayed()));
    
  • 点击对话框按钮,请执行以下操作(button1 - 确定,button2 - 取消):

  • onView(withId(android.R.id.button1)).perform(click());
    

    更新

  • 我认为这是可能的,因为Espresso具有多窗口支持
  • 不确定如何点击自定义对话框视图之外的区域,但是要检查它是否正在显示,您必须创建自定义matcher并在其中进行检查。

  • 3
    对于一个 ProgressDialog,第一步对我没有起作用。我只是想验证对话框的标题和消息。 - Tim Boland
    1
    浓缩咖啡和静态导入有什么关系?这些方法是从哪些类中来的?为什么你在Stack Overflow答案中使用静态导入? - user3629714
    3
    每个 Espresso 教程都使用静态导入。我建议你只是适应它(尽管我理解你的沮丧)。这个链接会有所帮助:https://google.github.io/android-testing-support-library/docs/ - AutonomousApps
    在第四步,您可以调用“pressBack();”来关闭对话框,而不是点击对话框外部。这相当于使用硬件返回按钮。 - Ethan
    @denys的项目已经被移动了。看起来链接已经失效了。 - Neon Warge

    83

    我目前使用这个,它似乎工作得很好。

    onView(withText(R.string.my_title))
        .inRoot(isDialog()) // <---
        .check(matches(isDisplayed()));
    

    完美。运行得像魔法一样! - Yuri Popiv
    这对我有效,而被接受的答案则不行。 - Ewan
    那是正确的答案。 - hannes ach

    30
    如果您有一个像这样的AlertDialog: enter image description here 您可以检查组件是否已显示:
    int titleId = mActivityTestRule.getActivity().getResources()
            .getIdentifier( "alertTitle", "id", "android" );
    
    onView(withId(titleId))
            .inRoot(isDialog())
            .check(matches(withText(R.string.my_title)))
            .check(matches(isDisplayed()));
    
    onView(withId(android.R.id.text1))
            .inRoot(isDialog())
            .check(matches(withText(R.string.my_message)))
            .check(matches(isDisplayed()));
    
    onView(withId(android.R.id.button2))
            .inRoot(isDialog())
            .check(matches(withText(android.R.string.no)))
            .check(matches(isDisplayed()));
    
    onView(withId(android.R.id.button3))
            .inRoot(isDialog())
            .check(matches(withText(android.R.string.yes)))
            .check(matches(isDisplayed()));
    

    并执行一个动作:

    onView(withId(android.R.id.button3)).perform(click());
    

    2
    我的代码中文本的ID是android.R.id.message,标题的隐藏ID是android.R.id.alertTitle - Jason Robinson
    2
    如果您使用来自AppCompat支持库的AlertDialog(或DialogFragment),请使用以下代码:int alertDialogTitleId = android.support.v7.appcompat.R.id.alertTitle; - Mr-IDE
    如果有人需要知道参数值,请参考 https://dev59.com/imUp5IYBdhLWcg3wJU5J#15488321。如果您不知道名称或 def 类型,请尝试 check() 一个不存在的视图并查看 logcat 上的布局层次结构。您将看到一堆这些 defType = name。例如,message=hello world,那么您的参数应该像这样:"hello world", "message", "your package name" - Jcorretjer

    4

    以防万一有人像我一样偶然发现了这个问题。所有答案仅适用于带有对话框按钮的对话框。不要尝试将其用于没有用户交互的进度对话框。Espresso会等待应用程序进入空闲状态。只要进度对话框可见,应用程序就不是空闲状态。


    3
    为了回答第4个问题,接受的答案没有提供,我修改了以下代码,这些代码是我在Stack Overflow上找到的(link),用于测试Toast是否显示。
    @NonNull
    public static ViewInteraction getRootView(@NonNull Activity activity, @IdRes int id) {
        return onView(withId(id)).inRoot(withDecorView(not(is(activity.getWindow().getDecorView()))));
    }
    

    传入的id是当前对话框中显示的View的ID。你也可以这样编写方法:
    @NonNull
    public static ViewInteraction getRootView(@NonNull Activity activity, @NonNull String text) {
        return onView(withText(text)).inRoot(withDecorView(not(is(activity.getWindow().getDecorView()))));
    }
    

    现在它正在寻找包含特定文本字符串的“View”。
    使用方法如下:
    getRootView(getActivity(), R.id.text_id).perform(click());
    

    3
    的ID在不同设备上可能不同,随着操作系统版本的改变,这些ID可能会发生变化。
    正确的方法是使用UIAutomator。请在您的build.gradle中包含UIAutomator依赖项。
    // Set this dependency to build and run UI Automator tests
      androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
    

    并且使用

    // Initialize UiDevice instance
    UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    
    // Search for correct button in the dialog.
    UiObject button = uiDevice.findObject(new UiSelector().text("ButtonText"));
    if (button.exists() && button.isEnabled()) {
        button.click();
    }
    

    2
    实际上,“android.R.id.button1”,“android.R.id.button2”和“android.R.id.button3”分别代表“positive”(肯定)、“neutral”(中立)和“negative”(否定),它们是全局符号,可以使用。如果您选择通过按钮文本来选择按钮 - 这完全没有问题 - 您不需要UIAutomator,而是可以使用Espresso的“onView(withText(“ButtonTest”))。perform(click())”完成相同的操作。 - Thomas Keller
    我使用了Robotium测试框架中的这个解决方案,能够轻松选择Android操作系统对话框按钮。节省了我大量时间。感谢jaydeepw! - Ray
    @ThomasKeller 我过去使用了button1、button2的id,但当我在多种设备上运行测试时,我的测试失败了。所显示的对话框是系统控件,而不是你的控件/UI。对于任何超出你的UI范围之外的内容,建议使用UIAutomator。 - JaydeepW
    就是这样了。非常感谢你。 - 1lb3r

    3

    如果你不想在对话框中检查特定的字符串,你可以使用hasWindowFocus()方法,如下所示:

    // Kotlin, with ActivityScenarioRule
    activityScenarioRule.scenario.onActivity { activity ->
       val dialogIsDisplayed = !activity.hasWindowFocus()
    }
    
    // Kotlin, with ActivityTestRule
    val dialogIsDisplayed = !activityTestRule.activity.hasWindowFocus()
    
    
    // Java 7, with ActivityScenarioRule
    activityScenarioRule.getScenario().onActivity(new ActivityScenario.ActivityAction<MyActivity>() {
        @Override
        public void perform(MyActivity activity) {
            boolean dialogIsDisplayed = !activity.hasWindowFocus();
        }
    });
    
    // Java, with ActivityTestRule
    boolean dialogIsDisplayed = !activityTestRule.getActivity().hasWindowFocus();
    

    来源:


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接