Android Espresso等待文本出现 [英] Android Espresso wait for text to appear

查看:164
本文介绍了Android Espresso等待文本出现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Espresso自动化作为聊天机器人的Android应用.我可以说我对Android应用程序自动化是完全陌生的. 现在,我正为等待而苦恼.如果我使用Thread.sleep,它可以很好地工作.但是,我想等到屏幕上出现特定的文本.我该怎么办?

I am trying to automate an Android app that is a chatbot using Espresso. I can say that I am completely new to Android app automation. Right now I am struggled with waiting. If I use Thread.sleep, it works perfectly fine. However, I would like to wait until a specific text appears on the screen. How can I do that?

@Rule
public ActivityTestRule<LoginActivity> mActivityTestRule = new ActivityTestRule<>(LoginActivity.class);

@Test
public void loginActivityTest() {
ViewInteraction loginName = onView(allOf(withId(R.id.text_edit_field),
childAtPosition(childAtPosition(withId(R.id.email_field),0), 1)));
loginName.perform(scrollTo(), replaceText("test@test.test"), closeSoftKeyboard());

ViewInteraction password= onView(allOf(withId(R.id.text_edit_field),
childAtPosition(childAtPosition(withId(R.id.password_field),0), 1)));
password.perform(scrollTo(), replaceText("12345678"), closeSoftKeyboard());

ViewInteraction singInButton = onView(allOf(withId(R.id.sign_in), withText("Sign In"),childAtPosition(childAtPosition(withId(R.id.scrollView), 0),2)));
singInButton .perform(scrollTo(), click());

//Here I need to wait for the text "Hi ..."

一些解释:在按下登录按钮后,聊天机器人会说"Hi".并提供更多信息.我想等待最后一条消息出现在屏幕上.

Some explanations: after pressing the sign in button, the chatbot says "Hi" and gives some more information. I would like to wait for the last one message to appear on the screen.

推荐答案

我喜欢上面@jeprubio的答案,但是我遇到了评论中提到的@desgraci相同的问题,他们的匹配者一直在寻找旧的视图,过时的rootview.尝试在测试中的活动之间进行转换时,经常会发生这种情况.

I like @jeprubio's answer above, however I ran into the same issue @desgraci mentioned in the comments, where their matcher is consistently looking for a view on an old, stale rootview. This happens frequently when trying to have transitions between activities in your test.

我对传统隐式等待"模式的实现位于下面的两个Kotlin文件中.

My implementation of the traditional "Implicit Wait" pattern lives in the two Kotlin files below.

EspressoExtensions.kt 包含函数searchFor,一旦在提供的rootview中找到匹配项,该函数就会返回ViewAction.

EspressoExtensions.kt contains a function searchFor which returns a ViewAction once a match has been found within supplied rootview.

class EspressoExtensions {

    companion object {

        /**
         * Perform action of waiting for a certain view within a single root view
         * @param matcher Generic Matcher used to find our view
         */
        fun searchFor(matcher: Matcher<View>): ViewAction {

            return object : ViewAction {

                override fun getConstraints(): Matcher<View> {
                    return isRoot()
                }

                override fun getDescription(): String {
                    return "searching for view $matcher in the root view"
                }

                override fun perform(uiController: UiController, view: View) {

                    var tries = 0
                    val childViews: Iterable<View> = TreeIterables.breadthFirstViewTraversal(view)

                    // Look for the match in the tree of childviews
                    childViews.forEach {
                        tries++
                        if (matcher.matches(it)) {
                            // found the view
                            return
                        }
                    }

                    throw NoMatchingViewException.Builder()
                        .withRootView(view)
                        .withViewMatcher(matcher)
                        .build()
                }
            }
        }
    }
}

BaseRobot.kt 调用searchFor()方法,检查是否返回了匹配器.如果没有返回匹配,它将休眠一小会儿,然后获取一个新的根进行匹配,直到尝试了X次,然后抛出异常,测试失败.对什么是机器人"感到困惑?查看 Jake Wharton的精彩演讲 .它与页面对象模型"模式非常相似

BaseRobot.kt calls the searchFor() method, checks if a matcher was returned. If no match is returned, it sleeps a tiny bit and then fetches a new root to match on until it has tried X times, then it throws an exception and the test fails. Confused about what a "Robot" is? Check out this fantastic talk by Jake Wharton about the Robot pattern. Its very similar to the Page Object Model pattern

open class BaseRobot {

    fun doOnView(matcher: Matcher<View>, vararg actions: ViewAction) {
        actions.forEach {
            waitForView(matcher).perform(it)
        }
    }

    fun assertOnView(matcher: Matcher<View>, vararg assertions: ViewAssertion) {
        assertions.forEach {
            waitForView(matcher).check(it)
        }
    }

    /**
     * Perform action of implicitly waiting for a certain view.
     * This differs from EspressoExtensions.searchFor in that,
     * upon failure to locate an element, it will fetch a new root view
     * in which to traverse searching for our @param match
     *
     * @param viewMatcher ViewMatcher used to find our view
     */
    fun waitForView(
        viewMatcher: Matcher<View>,
        waitMillis: Int = 5000,
        waitMillisPerTry: Long = 100
    ): ViewInteraction {

        // Derive the max tries
        val maxTries = waitMillis / waitMillisPerTry.toInt()

        var tries = 0

        for (i in 0..maxTries)
            try {
                // Track the amount of times we've tried
                tries++

                // Search the root for the view
                onView(isRoot()).perform(searchFor(viewMatcher))

                // If we're here, we found our view. Now return it
                return onView(viewMatcher)

            } catch (e: Exception) {

                if (tries == maxTries) {
                    throw e
                }
                sleep(waitMillisPerTry)
            }

        throw Exception("Error finding a view matching $viewMatcher")
    }
}

要使用它

// Click on element withId
BaseRobot().doOnView(withId(R.id.viewIWantToFind, click())

// Assert element withId is displayed
BaseRobot().assertOnView(withId(R.id.viewIWantToFind, matches(isDisplayed()))

我知道 IdlingResource 是Google宣讲的处理异步事件的方法在Espresso测试中,但通常要求您将测试特定的代码(即挂钩)嵌入到您的应用程序代码中才能同步测试.对于我来说,这似乎很奇怪,并且与一个拥有成熟应用程序且每天都有多个开发人员提交代码的团队一起工作,似乎只是为了进行测试而在应用程序中各处闲置资源进行改造将是很多额外的工作.就我个人而言,我更喜欢将应用程序和测试代码尽可能地分开. /end rant

I know that IdlingResource is what Google preaches to handle asynchronous events in Espresso testing, but it usually requires that you have test specific code (i.e hooks) embedded within your app code in order to synchronize the tests. That seems weird to me, and working on a team with a mature app and multiple developers committing code everyday, it seems like it would be a lot of extra work to retrofit idling resources everywhere in the app just for the sake of tests. Personally, I prefer to keep the app and test code as separate as possible. /end rant

这篇关于Android Espresso等待文本出现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆