46

我正在尝试检查 Espresso 是否显示视图。这是一些伪代码来显示我正在尝试的内容:

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

但我的问题是.check(doesNotExist())不返回布尔值。这只是一个断言。使用 UiAutomator,我可以做这样的事情:

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

8 回答 8

102

测试中的条件逻辑是不可取的。考虑到这一点,Espresso 的 API 旨在引导测试作者远离它(通过明确的测试操作和断言)。

话虽如此,您仍然可以通过实现自己的 ViewAction 并将 isDisplayed 检查(在 perform 方法中)捕获到 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
    }
于 2013-12-28T05:02:01.140 回答
11

我认为模仿 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'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;
    }
}

没有分支也exists可以非常简单地实现:

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

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

尽管大多数时候它还是值得检查的matches(isDisplayed())

于 2016-09-11T12:44:57.137 回答
9

我们需要这个功能,我最终在下面实现了它:

https://github.com/marcosdiez/espresso_clone

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

我希望它对你有用。

于 2014-03-12T17:09:00.173 回答
9

你也检查下面的代码。如果显示视图,它将单击,否则它将继续。

onView(withText("OK")).withFailureHandler(new FailureHandler() {
        @Override
        public void handle(Throwable error, Matcher<View> viewMatcher){
        }
    }).check(matches(isDisplayed())).perform(customClick());
于 2016-09-01T16:30:28.983 回答
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 中的哪个视图。

于 2017-03-23T22:38:50.700 回答
4

为什么没人提到:

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

只需not在 dosNotExist 之前添加。但是如果你经常使用这个逻辑,最好使用自定义匹配器。

于 2019-09-17T06:12:44.550 回答
2

我认为 Espresso 想要你做的是改变你使用的逻辑doesNotExist()

我有例如

        onView(snackBarMatcher).check(doesNotExist())

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


于 2019-05-22T13:05:10.787 回答
2

自从这个问题被提出以来已经有一段时间了,但由于它是谷歌在搜索确保视图存在的方法时最热门的问题之一,因此在对 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
    }
}

有了这个,您可以获取truefalse来自onView(withId(id)).exists(),或者安全地捕获 RuntimeException 并返回false

通常一个简单的检查.exists()就足够了,但在某些情况下,比如当您删除 ListView 项目直到 non 离开时 -> 当最后一个项目被删除时,ListView 可能不再存在,然后在尝试检查它是否存在。

3:通过上述实现,检查是否存在任何视图是安全的,因为在RuntimeException幕后处理得很好:

if(viewExists(R.id.something)) {
    //do something
}
//do something else
于 2019-01-30T11:11:54.190 回答