101

I'm trying to write some tests with the new android-test-kit (Espresso). But I can't find any information on how to check if a dialog is displayed and perform some actions on it (like clicking the positive and negative buttons, e.t.c.). Note that a dialog may be also displayed by a WebView, not by the application it self.

Any help would be appreciated. I just need a link, or some example code for the basics:

  1. Check if a dialog appears
  2. Perform clicks on dialog buttons
  3. Interact with the dialog's inner view (if it's a custom view)
  4. Preform clicks outside the dialog, and check if it's displaying or not (for example if setCancelable(false) was called on the dialog builder and we want to check that)

Thank you in advice!

Serj Lotutovici
  • 4,370
  • 5
  • 32
  • 41

7 Answers7

143
  1. To verify if dialog appears you can simply check if View with a text that present inside the dialog is shown:

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

    or, based on text with id

    onView(withId(R.id.myDialogTextId)).check(matches(allOf(withText(myDialogText), isDisplayed()));
    
  2. To click on dialogs button do this (button1 - OK, button2 - Cancel):

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

    UPDATE

  3. I think is possible since Espresso has multi window support.
  4. Not sure about clicking outside the custom dialog view but for checking if it is displaying or not you have to create your custom matcher and check inside it.
denys
  • 6,834
  • 3
  • 37
  • 36
  • 3
    Step 1 didn't work for me on a ProgressDialog. Just trying to validate the Title and Message of the dialog – Tim Boland Sep 28 '14 at 02:28
  • 1
    What is it with espresso and static imports? Which classes are those methods coming from? Why are you using static imports on a stack overflow answer? –  Oct 15 '15 at 08:28
  • 3
    @jvrodrigues every single Espresso tutorial uses static imports. I'd suggest you just get used to it (though I understand the frustration). This helps: https://google.github.io/android-testing-support-library/docs/ – AutonomousApps Dec 08 '15 at 22:05
  • For step 4 instead of clicking outside the dialog you can call "pressBack();" which dismisses the dialog. it's the equivalent of using the hardware back button. – Ethan Jul 14 '16 at 15:47
  • @denys project has been moved. it seems the link is dead. – Neon Warge Oct 15 '16 at 14:44
82

I currently use this and it seems to work fine.

onView(withText(R.string.my_title))
    .inRoot(isDialog()) // <---
    .check(matches(isDisplayed()));
passsy
  • 5,162
  • 4
  • 39
  • 65
kiruwka
  • 9,250
  • 4
  • 30
  • 41
30

If you have an AlertDialog like that:

enter image description here

You can check if the components are displayed:

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()));

and perform an action:

onView(withId(android.R.id.button3)).perform(click());
Luiz Augusto
  • 827
  • 10
  • 8
  • 2
    The text in my case had id `android.R.id.message` and the title had a hidden id of `android.R.id.alertTitle`. – Jason Robinson Aug 09 '16 at 22:35
  • 2
    If you use AlertDialog (or DialogFragment) that comes from the AppCompat support library, use this: `int alertDialogTitleId = android.support.v7.appcompat.R.id.alertTitle;` – Mr-IDE Apr 09 '18 at 10:15
  • In case anyone needs to know the parameter values, https://stackoverflow.com/a/15488321/1713366. If you don't know the name or the def type, try to `check()` a view that doesn't exist and look at the layout hierarchy on logcat. You'll see a bunch of these `defType = name`. For example, `message=hello world`, then your parameter should look like`"hello world", "message", "your package name"` – Jcorretjer Jan 05 '21 at 22:11
4

Just in case anyone stumbles across this question like I did. All the answers will only work for dialogs WITH dialog buttons. Do not try and use this for progress dialogs without user interaction. Espresso keeps waiting for the app to enter an idle state. As long as the progress dialog is visible the app is not idle.

Diana Farin
  • 580
  • 1
  • 4
  • 10
3

To answer question 4, which the accepted answer does not, I modified the following code, which I found here on Stack Overflow (link) for testing whether a Toast was displayed.

@NonNull
public static ViewInteraction getRootView(@NonNull Activity activity, @IdRes int id) {
    return onView(withId(id)).inRoot(withDecorView(not(is(activity.getWindow().getDecorView()))));
}

The id passed in is the id of a View currently displayed in your dialog. You could also write the method like so:

@NonNull
public static ViewInteraction getRootView(@NonNull Activity activity, @NonNull String text) {
    return onView(withText(text)).inRoot(withDecorView(not(is(activity.getWindow().getDecorView()))));
}

And now it's looking for a View containing a particular text string.

Use it like so:

getRootView(getActivity(), R.id.text_id).perform(click());
Community
  • 1
  • 1
AutonomousApps
  • 4,229
  • 4
  • 32
  • 42
3

The button Ids R.id.button1 and R.id.button2 are not going to be same across devices. The Ids may change with the OS versions.

The correct way to achieve this is to use UIAutomator. Include UIAutomator dependency in your build.gradle

// Set this dependency to build and run UI Automator tests
  androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'

and use

// 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();
}
JaydeepW
  • 3,237
  • 1
  • 25
  • 27
  • 2
    Actually, `android.R.id.button1`, `android.R.id.button2` and `android.R.id.button3` for "positive", "neutral" and "negative", are global symbols that _can_ be used. If you choose to select buttons via their text - which is totally fine - you do not need UIAutomator, but can do the same with Espresso's `onView(withText("ButtonTest")).perform(click())`. – Thomas Keller Apr 14 '16 at 22:47
  • I used this solution with a Robotium test framework and I was able to select Android OS dialog box buttons with ease. Saved me a bunch of time. Thanks jaydeepw! – Ray Apr 27 '16 at 00:37
  • @ThomasKeller I have used button1, button2 ids in the past, my tests broken when I ran them on variety of devices. The dialog shown is the system control. Not your control / UI. For anything outside your UI, UIAutomator is recommended. – JaydeepW Apr 28 '16 at 06:11
  • That's it. Thank you a lot. – 1lb3r Jan 29 '20 at 10:06
2

If you don't want to check for a specific String in a dialog, you can use hasWindowFocus(), like this:

// 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();

Sources:

Mr-IDE
  • 7,051
  • 1
  • 53
  • 59