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

查看:37
本文介绍了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 ..."

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

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,一旦在提供的根视图中找到匹配项,它就会返回一个 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 次,然后它会抛出异常并且测试失败.对什么是机器人"感到困惑?查看杰克沃顿关于机器人模式的精彩演讲.它与页面对象模型模式非常相似

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 测试中,但它通常需要您在应用程序代码中嵌入测试特定代码(即钩子)以同步测试.这对我来说似乎很奇怪,在一个拥有成熟应用程序和多个开发人员每天提交代码的团队中工作,似乎为了测试而改造应用程序中无处不在的空闲资源似乎需要很多额外的工作.就个人而言,我更喜欢将应用程序和测试代码尽可能分开./结束咆哮

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天全站免登陆