首页 > 解决方案 > 如何在没有 IdlingResource 的情况下在 Espresso 中等待异步任务

问题描述

我有以下浓缩咖啡代码:

@Test
fun backgroundWorkDisplaysTextAfterLoading() {
    onView(withId(R.id.btn_next_fragment)).perform(click())
    onView(withId(R.id.btn_background_work)).perform(click())

    onView(withId(R.id.hiddenTextView)).check(matches(isDisplayed()))
}

单击 btn_background_work 时,有一个服务调用会在 2 秒内返回响应,然后 hiddenTextview 将变得可见。

如何让 Espresso 等待执行 ViewAssertion ( check(matches(isDisplayed)) ),直到该服务调用返回一个值?服务调用通过 Retrofit 完成,服务调用在 Schedulers.io() 线程上完成。

我无法触及生产代码,因此在生产代码中实现 IdlingResources 是不可能的。

任何帮助表示赞赏!

标签: androidtestingandroid-espressoui-testing

解决方案


您仍然可以在IdlingResource不接触生产代码的情况下实施。您只需要创建一个IdlingResource侦听的回调ViewTreeObserver.OnDrawListener

private class ViewPropertyChangeCallback(private val matcher: Matcher<View>, private val view: View) : IdlingResource, ViewTreeObserver.OnDrawListener {

    private lateinit var callback: IdlingResource.ResourceCallback
    private var matched = false

    override fun getName() = "View property change callback"

    override fun isIdleNow() = matched

    override fun registerIdleTransitionCallback(callback: IdlingResource.ResourceCallback) {
        this.callback = callback
    }

    override fun onDraw() {
        matched = matcher.matches(view)
        callback.onTransitionToIdle()
    }
}

然后创建一个自定义ViewAction等待匹配:

fun waitUntil(matcher: Matcher<View>): ViewAction = object : ViewAction {

    override fun getConstraints(): Matcher<View> {
        return any(View::class.java)
    }

    override fun getDescription(): String {
        return StringDescription().let {
            matcher.describeTo(it)
            "wait until: $it"
        }
    }

    override fun perform(uiController: UiController, view: View) {
        if (!matcher.matches(view)) {
            ViewPropertyChangeCallback(matcher, view).run {
                try {
                    IdlingRegistry.getInstance().register(this)
                    view.viewTreeObserver.addOnDrawListener(this)
                    uiController.loopMainThreadUntilIdle()
                } finally {
                    view.viewTreeObserver.removeOnDrawListener(this)
                    IdlingRegistry.getInstance().unregister(this)
                }
            }
        }
    }
}

并更新您的测试以使用该操作:

onView(withId(R.id.hiddenTextView)).perform(waitUntil(isDisplayed()))
// or
onView(withId(R.id.hiddenTextView)).perform(waitUntil(withEffectiveVisibility(VISIBLE)))
    .check(matches(isDisplayed()))

没有IdlingResource,Thread.sleep(...)可能是您的下一个选择,但它会不稳定或效率低下。


推荐阅读