Espresso: return boolean if view exists

I am trying to check if a view is displayed using 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())

not returning boolean. This is just a statement. With the UiAutomator I was able to do something like this:

 if (UiAutomator.getbyId(SomeId).exists()){
      .....
   }

      

+36


source to share


7 replies


Conditional logic is not desirable in tests . With this in mind, the Espresso API was designed to take the test author away from him (by being explicit with test actions and assertions).

Having said that, you can still achieve the above by implementing your own ViewAction and writing the isDisplayed check (inside the execute method) to the AtomicBoolean.

Another less elegant option is to catch the exception thrown on unsuccessful validation:



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

      

Kotlin version with 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
    }

      

+73


source


You can also check the code below. If a view is displayed, it will click again when it passes.



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

      

+9


source


I think you can do this to mimic the UIAutomator:
(Although, I suggest rethinking your approach so that there are no strings attached.)

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 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 very simply:

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

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

      

Most of the time though, it's worth checking matches(isDisplayed())

out anyway.

+9


source


We need this functionality and I've implemented it below:

https://github.com/marcosdiez/espresso_clone

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

      

I hope this is helpful to you.

+8


source


Based on Dhiren Mudgil's answer, I wrote 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 am using this to determine which view in the ViewFlipper is currently being displayed.

+4


source


It has been a while since this issue was reported, but since it is one of the most popular ways on Google when searching for ways to make sure a view is present, before doing any action on it in Espresso, I would like to share my main method Deal with it.

1. Start by writing an extension for 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 the base class (which will be used in all test classes):

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

      

When doing so, you either get the value from true

or false

from onView(withId(id)).exists()

, or safely catch the RuntimeException and return false

.

Usually a simple check for .exists()

would be sufficient, but in some cases, such as when you remove the ListView items until there is no remaining -> when the last item is removed the ListView may no longer be present, then an exception is thrown when trying to check if there is whether he.

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

it handles well behind the scene:

if(viewExists(R.id.something)) {
    //do something
}
//do something else

      

+2


source


I think what Espresso wants you to do is 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()))



      

0


source







All Articles