Espresso: return boolean, если вид существует
Я пытаюсь проверить, отображается ли представление с помощью Espresso. Вот какой-то псевдо-код, чтобы показать, что я пытаюсь:
if (!Espresso.onView(withId(R.id.someID)).check(doesNotExist()){
// then do something
} else {
// do nothing, or what have you
}
Но моя проблема .check(doesNotExist())
не возвращает boolean. Это просто утверждение. С помощью UiAutomator я смог сделать что-то вроде этого:
if (UiAutomator.getbyId(SomeId).exists()){
.....
}
Ответы
Ответ 1
Условная логика в тестах нежелательна. Имея это в виду, Espresso API был разработан для того, чтобы увести автора теста от него (будучи явным с действиями и утверждениями теста).
Сказав это, вы все равно можете достичь вышеупомянутого, реализовав свой собственный ViewAction и записав проверку isDisplayed (внутри метода execute) в AtomicBoolean.
Еще один менее элегантный вариант - поймать исключение, которое выдается при неудачной проверке:
try {
onView(withText("my button")).check(matches(isDisplayed()));
//view is displayed logic
} catch (NoMatchingViewException e) {
//view not displayed logic
}
Версия Kotlin с функцией расширения:
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
}
Ответ 2
Вы также можете проверить код ниже. Если отображается представление, он будет щелкнуть еще раз, когда он пройдет.
onView(withText("OK")).withFailureHandler(new FailureHandler() {
@Override
public void handle(Throwable error, Matcher<View> viewMatcher){
}
}).check(matches(isDisplayed())).perform(customClick());
Ответ 3
Я думаю, что для имитации UIAutomator вы можете это сделать:
(Хотя, я предлагаю переосмыслить ваш подход, чтобы не было никаких условий.)
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;
}
}
Также exists
без разветвления может быть реализовано очень просто:
onView(withBlah()).check(exists()); // the opposite of doesNotExist()
public static ViewAssertion exists() {
return matches(anything());
}
Хотя большую часть времени стоит проверить matches(isDisplayed())
в любом случае.
Ответ 4
Нам нужна эта функциональность, и я реализовал ее ниже:
https://github.com/marcosdiez/espresso_clone
if(onView(withText("click OK to Continue")).exists()){
doSomething();
} else {
doSomethingElse();
}
Я надеюсь, что это полезно для вас.
Ответ 5
На основании ответа Dhiren Mudgil я написал следующий метод:
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];
}
Я использую это, чтобы определить, какой вид в ViewFlipper отображается в настоящее время.
Ответ 6
Прошло некоторое время с тех пор, как эта проблема была заявлена, но, поскольку она является одной из самых популярных в Google при поиске способов убедиться, что представление присутствует, перед выполнением каких-либо действий с ним в Espresso я хотел бы поделиться своими основными способ справиться с этим.
1. Начните с написания расширения для 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. Создайте простой метод справки в базовом классе (который будет использоваться во всех тестовых классах):
fun viewExists(id: Int): Boolean {
return try {
onView(withId(id)).exists()
} catch (e: RuntimeException) {
false
}
}
При этом вы либо получаете значение true
или false
из onView(withId(id)).exists()
, либо безопасно перехватываете RuntimeException и возвращаете false
.
Обычно простой проверки для .exists()
было бы достаточно, но в некоторых случаях, например, когда вы удаляете элементы ListView до тех пор, пока не осталось не осталось → когда последний элемент удален, ListView может больше не присутствовать, тогда возникает исключение бросается при попытке проверить, существует ли он.
3: С вышеупомянутой реализацией безопасно проверить, существует ли какое-либо представление, так как RuntimeException
хорошо обрабатывается позади сцены:
if(viewExists(R.id.something)) {
//do something
}
//do something else
Ответ 7
Я думаю, что то, что Espresso хочет, чтобы вы сделали, - это измените свою логику, чтобы использовать doesNotExist()
У меня например
onView(snackBarMatcher).check(doesNotExist())
onView(withId(R.id.button)).perform(click())
onView(snackBarMatcher).check(matches(isDisplayed()))