13

I have a method that checks several conditions, and calls another activity when they are satisfied. When a condition isn't satisfied, it should display an error dialog (which is currently using a DialogFragment to display an alert dialog). The method looks something like this:

void checkAndCall() {
    CustomObject o1 = null;
    try {
        o1 = CustomObject.parse(editText1.getText().toString());
    } catch (CustomException e) {
        handleBadCase(e);
        return;
    }

    CustomObject o2 = null;
    try {
        o2 = CustomObject.parse(editText2.getText().toString());
    } catch (CustomException e) {
        handleBadCase(e);
        return;
    }

    callOtherActivity();
}

Unfortunately, I had forgotten a return statement, which caused the method to drop to the next check (which failed) and display two error dialogs. I want to make sure that this doesn't happen again, so have written a test for it.

My test looks like this:

public class TestClass {

    @Rule
    public ActivityTestRule<MyActivity> mActivityRule = new ActivityTestRule<>(MyActivity.class);

    @Test
    public void onlyOneDialogAppearsWithEmptySolve() {
        /* Hit solve with no text entered */
        onView(withId(R.id.solve_button)).perform(click());

        /* Check that dialog is displayed */
        onView(isRoot()).inRoot(isDialog()).check(matches(isDisplayed()));

        /* Press cancel */
        onView(withText(getString(R.string.cancel))).perform(click());

        /* No dialog is displayed */
        onView(isRoot()).inRoot(isDialog()).check(doesNotExist());      
    }
} 

I had thought that onView(isRoot()).inRoot(isDialog()) would match the root view of any dialog. However, this seems to just hang if it doesn't match anything. Thus, when the test should be satisfied (only one dialog appears and is cancelled), it hangs. If we comment out that line, and do not display any dialogs, the test hangs on the Check that dialog is displayed line.

I would prefer not to match against dialog text, as they may all be different. I still need to be sure that only one occurs if any do. Right now, I am taking advantage of all of them having a "Cancel" button to match against. However, I would prefer to not rely on this.

Is there a method for saying Is any dialog displayed? and Are no dialogs displayed? Why do the checks that I have cause this hanging?

Matthew
  • 7,440
  • 1
  • 24
  • 49
  • 5
    try this `onView(isRoot()).inRoot(isDialog()).noActivity()` instead `doesNotExist()` – Moinkhan Dec 05 '19 at 13:47
  • @Moinkhan this solved the issue for me thanks – Louis Mar 06 '20 at 10:33
  • 4
    @Moinkhan it doesn't fail when the dialog exists (is not dismissed) – fikr4n Jan 14 '22 at 07:23
  • This is a perfect storm of issues, (1) it seems `check(doesNotExist())` doesn't work when used together with `inRoot`, (2) there is no easy way to check if a view is inside a dialog (and `isDialog` matches against a `Root` (Espresso internal class) not a `View`, and uses some reflection to access some private hidden fields to execute its logic against). I know you said you want to avoid this, but honestly the best way is to just match against some known element inside the dialog. for example I'm using `AlertDialog` so I use `onView(withId(android.R.id.button1)).check(doesNotExist())` – Adam Burley Aug 18 '22 at 16:58
  • NOTE: For other roots such as `isPlatformPopup` I found that matching against a known view "does not exist" passed even if the window was visible, when `inRoot` was omitted. It seems that in some scenarios, Espresso **only** matches the view in non-standard roots when `inRoot` is specified. To work around this, eventually I had to write a try-catch and catch the `NoMatchingRootException`, which still causes the thread to block unfortunately but is the only solution that works – Adam Burley Sep 29 '22 at 10:50
  • see https://stackoverflow.com/a/73894325/191761 – Adam Burley Sep 29 '22 at 22:50

1 Answers1

0

Since this is conditional / navigational logic I would have put this into a ViewModel and tested it in a unit test. With mocking frameworks (e.g. mockito) you can verify that certain methods are only called once. Not quite the answer to your question but should help you avoid some unwanted invocation bugs.

Oliver Metz
  • 2,712
  • 2
  • 20
  • 32