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()){
.....
}
source to share
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
}
source to share
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());
source to share
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.
source to share
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.
source to share
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.
source to share
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
source to share