Home » Android » testing – Android Espresso: How to check that Toast message is NOT shown?

testing – Android Espresso: How to check that Toast message is NOT shown?

Posted by: admin June 16, 2020 Leave a comment

Questions:

I’m working now in my functional tests and in one of them I have to test that a toast message is NOT shown. Considering this is the code I’m using to check if the toast message is shown (this code works):

onView(withText(R.string.my_toast_message))
        .inRoot(withDecorView(not(getActivity().getWindow().getDecorView())))
        .check(matches(isDisplayed()));

below you can find the code I’m using to check that a toast message is NOT shown (none of them work):

Approach one:

onView(withText(R.string.error_invalid_login))
        .inRoot(withDecorView(not(getActivity().getWindow().getDecorView())))
        .check(matches(not(isDisplayed())));

Approach two:

onView(withText(R.string.error_invalid_login))
        .inRoot(withDecorView(not(getActivity().getWindow().getDecorView())))
        .check(doesNotExist());

Any idea about how can I check that a toast message is not shown would be really appreciated 🙂

How to&Answers:

The best way to test toast message in espresso is using a custom matcher:

public class ToastMatcher extends TypeSafeMatcher<Root> {
    @Override public void describeTo(Description description) {
        description.appendText("is toast");
    }

    @Override public boolean matchesSafely(Root root) {
        int type = root.getWindowLayoutParams().get().type;
        if ((type == WindowManager.LayoutParams.TYPE_TOAST)) {
            IBinder windowToken = root.getDecorView().getWindowToken();
            IBinder appToken = root.getDecorView().getApplicationWindowToken();
            if (windowToken == appToken) {
                //means this window isn't contained by any other windows. 
            }
        }
        return false;
    }
}

This you can use in your test case:

  1. Test if the Toast Message is Displayed

    onView(withText(R.string.message)).inRoot(new ToastMatcher())
    .check(matches(isDisplayed()));
    
  2. Test if the Toast Message is not Displayed

    onView(withText(R.string.message)).inRoot(new ToastMatcher())
    .check(matches(not(isDisplayed())));
    
  3. Test id the Toast contains specific Text Message

    onView(withText(R.string.message)).inRoot(new ToastMatcher())
    .check(matches(withText("Invalid Name"));
    

I copied this answer from my blog –
http://qaautomated.blogspot.in/2016/01/how-to-test-toast-message-using-espresso.html

Answer:

It is required to catch the case when the toast does not exist, for which a NoMatchingRootException is thrown. Below shows the “Espresso way” of catching that.

public static Matcher<Root> isToast() {
    return new WindowManagerLayoutParamTypeMatcher("is toast", WindowManager.LayoutParams.TYPE_TOAST);
}
public static void assertNoToastIsDisplayed() {
    onView(isRoot())
            .inRoot(isToast())
            .withFailureHandler(new PassMissingRoot())
            .check(matches(not(anything("toast root existed"))))
    ;
}

A quick (self-)test that uses the above:

@Test public void testToastMessage() {
    Toast toast = createToast("Hello Toast!");
    assertNoToastIsDisplayed();
    toast.show();
    onView(withId(android.R.id.message))
            .inRoot(isToast())
            .check(matches(withText(containsStringIgnoringCase("hello"))));
    toast.cancel();
    assertNoToastIsDisplayed();
}

private Toast createToast(final String message) {
    final AtomicReference<Toast> toast = new AtomicReference<>();
    InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
        @SuppressLint("ShowToast") // will be shown later
        @Override public void run() {
            toast.set(Toast.makeText(InstrumentationRegistry.getContext(), message, Toast.LENGTH_LONG));
        }
    });
    return toast.get();
}

The magical reusable helper classes:

public class PassMissingRoot implements FailureHandler {
    private final FailureHandler defaultHandler
            = new DefaultFailureHandler(InstrumentationRegistry.getTargetContext());
    @Override public void handle(Throwable error, Matcher<View> viewMatcher) {
        if (!(error instanceof NoMatchingRootException)) {
            defaultHandler.handle(error, viewMatcher);
        }
    }
}

public class WindowManagerLayoutParamTypeMatcher extends TypeSafeMatcher<Root> {
    private final String description;
    private final int type;
    private final boolean expectedWindowTokenMatch;
    public WindowManagerLayoutParamTypeMatcher(String description, int type) {
        this(description, type, true);
    }
    public WindowManagerLayoutParamTypeMatcher(String description, int type, boolean expectedWindowTokenMatch) {
        this.description = description;
        this.type = type;
        this.expectedWindowTokenMatch = expectedWindowTokenMatch;
    }
    @Override public void describeTo(Description description) {
        description.appendText(this.description);
    }
    @Override public boolean matchesSafely(Root root) {
        if (type == root.getWindowLayoutParams().get().type) {
            IBinder windowToken = root.getDecorView().getWindowToken();
            IBinder appToken = root.getDecorView().getApplicationWindowToken();
            if (windowToken == appToken == expectedWindowTokenMatch) {
                // windowToken == appToken means this window isn't contained by any other windows.
                // if it was a window for an activity, it would have TYPE_BASE_APPLICATION.
                return true;
            }
        }
        return false;
    }
}

Answer:

This works

boolean exceptionCaptured = false;
try {
  onView(withText(R.string.error_invalid_login))
          .inRoot(withDecorView(not(is(getActivity().getWindow().getDecorView()))))
          .check(doesNotExist());
}catch (NoMatchingRootException e){
  exceptionCaptured = true;
}finally {
  assertTrue(exceptionCaptured);
}

Answer:

Seems that such simple check is impossible with espresso if you have not only toast but a PopupWindow, for example.

For this case is suggest just to give up with espresso here and use UiAutomator for this assertion

val device: UiDevice
   get() = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())

fun assertPopupIsNotDisplayed() {
    device.waitForIdle()
    assertFalse(device.hasObject(By.text(yourText))))
}

fun assertPopupIsDisplayed() {
    device.waitForIdle()
    assertTrue(device.hasObject(By.text(yourText))))
}

Answer:

I know its late but may be this will help somebody else.

    onView(withText("Test")).inRoot(withDecorView(not(mActivityRule.getActivity().getWindow().getDecorView())))
            .check(doesNotExist());

Answer:

Like @anuja jain’s answer but if you get the NoMatchingRootException you can comment out the if ((type == WindowManager.LayoutParams.TYPE_TOAST)) check and add the return true; line to the inner if block.

Answer:

Try with below solution

onView(withId(android.R.id.message))
                .inRoot(withDecorView(not(is(mRule.getActivity().getWindow().getDecorView()))))
                .check(matches(withText("Some message")));

Answer:

You can look at the source code here and create your own view matcher that does exactly the opposite.