Android Espresso: How to check if Toast message is not showing?

Now I work in my functional tests, and in one of them I need to check that the toast message is NOT shown. Given that this is the code that I use to check if a message is displayed with a toast (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 that I use to check that the toast message is NOT displayed (none of them work):

Approach one:

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

Go to two:

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

Any idea on how I can verify that the message with the toast is not showing up would be really appreciated :)

+5
source share
7 answers

The best way to check the toast message in espresso is to use a custom socket:

 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 can be used in a test case:

  • Check if Toast is displayed

     onView(withText(R.string.message)).inRoot(new ToastMatcher()) .check(matches(isDisplayed())); 
  • Check if a toast message is displayed

     onView(withText(R.string.message)).inRoot(new ToastMatcher()) .check(matches(not(isDisplayed()))); 
  • Toast test identifier contains a 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

+4
source

It is required to catch the case when the toast does not exist, for which NoMatchingRootException . The following is the Espresso method for this.

 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-diagnostic) test that uses the following:

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

Reuse magic 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; } } 
+3
source

It 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); } 
+2
source

I know him late, but maybe this will help someone else.

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

Like the @anuja jain answer, but if you get a NoMatchingRootException , you can comment on the if ((type == WindowManager.LayoutParams.TYPE_TOAST)) check and add the line return true; into the if block.

0
source

Try using the solution below.

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

Here you can look at the source code and create your own hit counter that does exactly the opposite.

-1
source

All Articles