Question

I am trying to check to see if a view is displayed with Espresso. Here is some pseudo code to show what I am trying:

if (!Espresso.onView(withId(R.id.someID)).check(doesNotExist()){
   // then do something
 } else {
   // do nothing, or what have you
 }

But my problem is .check(doesNotExist()) does not return boolean. It is just an assertion. With UiAutomator I was able to just do something like so:

 if (UiAutomator.getbyId(SomeId).exists()){
      .....
   }
Was it helpful?

Solution

Conditional logic in tests is undesirable. With that in mind, Espresso's API was designed to guide the test author away from it (by being explicit with test actions and assertions).

Having said that, you can still achieve the above by implementing your own ViewAction and capturing the isDisplayed check (inside the perform method) into an AtomicBoolean.

Another less elegant option - catch the exception that gets thrown by failed check:

    try {
        onView(withText("my button")).check(matches(isDisplayed()));
        //view is displayed logic
    } catch (NoMatchingViewException e) {
        //view not displayed logic
    }

Kotlin version with an extension function:

    fun ViewInteraction.isDisplayed(): Boolean {
        try {
            check(matches(ViewMatchers.isDisplayed()))
            return true
        } catch (e: NoMatchingViewException) {
            return false
        }
    }

    if(onView(withText("my button")).isDisplayed()) {
        //view is displayed logic
    } else {
        //view not displayed logic
    }

OTHER TIPS

I think to mimic UIAutomator you can do this:
(Though, I suggest rethinking your approach to have no conditions.)

ViewInteraction view = onView(withBlah(...)); // supports .inRoot(...) as well
if (exists(view)) {
     view.perform(...);
}

@CheckResult
public static boolean exists(ViewInteraction interaction) {
    try {
        interaction.perform(new ViewAction() {
            @Override public Matcher<View> getConstraints() {
                return any(View.class);
            }
            @Override public String getDescription() {
                return "check for existence";
            }
            @Override public void perform(UiController uiController, View view) {
                // no op, if this is run, then the execution will continue after .perform(...)
            }
        });
        return true;
    } catch (AmbiguousViewMatcherException ex) {
        // if there's any interaction later with the same matcher, that'll fail anyway
        return true; // we found more than one
    } catch (NoMatchingViewException ex) {
        return false;
    } catch (NoMatchingRootException ex) {
        // optional depending on what you think "exists" means
        return false;
    }
}

Also exists without branching can be implemented really simple:

onView(withBlah()).check(exists()); // the opposite of doesNotExist()

public static ViewAssertion exists() {
    return matches(anything());
}

Though most of the time it's worth checking for matches(isDisplayed()) anyway.

We need that functionality and I ended up implementing it below:

https://github.com/marcosdiez/espresso_clone

if(onView(withText("click OK to Continue")).exists()){ 
    doSomething(); 
} else { 
   doSomethingElse(); 
}

I hope it is useful for you.

You check with the below code also. If view is displayed it will click else it will pass on.

onView(withText("OK")).withFailureHandler(new FailureHandler() {
        @Override
        public void handle(Throwable error, Matcher<View> viewMatcher){
        }
    }).check(matches(isDisplayed())).perform(customClick());

Based on the answer by Dhiren Mudgil, I ended up writing the following method:

public static boolean viewIsDisplayed(int viewId)
{
    final boolean[] isDisplayed = {true};
    onView(withId(viewId)).withFailureHandler(new FailureHandler()
    {
        @Override
        public void handle(Throwable error, Matcher<View> viewMatcher)
        {
            isDisplayed[0] = false;
        }
    }).check(matches(isDisplayed()));
    return isDisplayed[0];
}

I'm using this to help determine which View in a ViewFlipper is currently displayed.

Why no one mentioned:

onView(withId(R.id.some_view_id)).check(matches(not(doesNotExist())))

just add not before doesNotExist. But if you use this logic a lot it's better to use a custom matcher.

I think that what Espresso wants you to do is to change your logic to use doesNotExist()

I have for example

        onView(snackBarMatcher).check(doesNotExist())

        onView(withId(R.id.button)).perform(click())
        onView(snackBarMatcher).check(matches(isDisplayed()))


It's been some time since this issue was stated, but as it is one of the top hit on Google when searching for ways to make sure a view is present, before doing any actions on it in Espresso, I would like to share my very basic way of handling this.

1: Start out by writing an extension to ViewInteraction:

fun ViewInteraction.exists(): Boolean {
val viewExists = AtomicReference<Boolean>()

this.perform(object : ViewAction {
    override fun perform(uiController: UiController?, view: View?) {
        viewExists.set(view != null)
    }

    override fun getConstraints(): Matcher<View>? {
        return Matchers.allOf(ViewMatchers.withEffectiveVisibility(
                ViewMatchers.Visibility.VISIBLE),
                ViewMatchers.isAssignableFrom(View::class.java))
    }

    override fun getDescription(): String {
        return "check if view exists"
    }
})
return viewExists.get()
}

2: Create a simple help method in your base class (to be used in all test classes):

fun viewExists(id: Int): Boolean {
    return try {
        onView(withId(id)).exists()
    } catch (e: RuntimeException) {
        false
    }
}

With this you either get true or false from onView(withId(id)).exists(), or safely catch the RuntimeException and return false.

Normally a simple check to .exists() would be sufficient, but in some cases, like when you are deleting ListView items until non is left -> when the last item is deleted, the ListView might no longer be present, then an Exception is thrown when trying to check if it exists.

3: With the above implementation, it is safe to check if any view exists, since the RuntimeException is handled nicely behind the scene:

if(viewExists(R.id.something)) {
    //do something
}
//do something else
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top