如何在 Espresso 测试中等待视图消失?

9
我有一个显示“加载中”字符串的TextView,并且我需要等待直到这个视图消失。由于这个方法是在IntentService中运行并在加载完成时发送广播,所以我无法获取Asynctask的句柄。
对于如何在Espresso测试中等待视图状态变化有什么想法吗?我还需要等待一些字符串的变化。我猜应该是类似的。
谢谢帮助。网络上没有太多示例或常见问题解答。

1
可能是Espresso - 断言具有异步加载数据的TextView的重复问题。 - Nizamudeen Sherif
3个回答

3
你可以定义一个ViewAction,它会每50毫秒(或您选择的其他时间)循环主线程,直到View的可见性变为View.GONE或经过最长时间。
按照以下步骤来实现这个目标。
第一步:
定义ViewAction,如下所示:
/**
 * A [ViewAction] that waits up to [timeout] milliseconds for a [View]'s visibility value to change to [View.GONE].
 */
class WaitUntilGoneAction(private val timeout: Long) : ViewAction {

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

    override fun getDescription(): String {
        return "wait up to $timeout milliseconds for the view to be gone"
    }

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

        val endTime = System.currentTimeMillis() + timeout

        do {
            if (view.visibility == View.GONE) return
            uiController.loopMainThreadForAtLeast(50)
        } while (System.currentTimeMillis() < endTime)

        throw PerformException.Builder()
            .withActionDescription(description)
            .withCause(TimeoutException("Waited $timeout milliseconds"))
            .withViewDescription(HumanReadables.describe(view))
            .build()
    }
}
第二步

定义一个函数,当调用时创建一个此ViewAction的实例,如下所示:

/**
 * @return a [WaitUntilGoneAction] instance created with the given [timeout] parameter.
 */
fun waitUntilGone(timeout: Long): ViewAction {
    return WaitUntilGoneAction(timeout)
}

第三步
在你的测试方法中调用这个ViewAction,如下所示:
onView(withId(R.id.loadingTextView)).perform(waitUntilGone(3000L))
下一步

以这个概念为基础,类似地创建一个WaitForTextAction类,它会等待TextView的文本更改为特定的值。然而,在这种情况下,您可能希望将getConstraints()函数返回的Matcherany(View::class.java)更改为any(TextView::class.java)


1
嗯,我认为ViewActions在主线程上运行,因此我们不希望让其在整个超时期间一直运行。也许最好让ViewAction只检查可见性一次,而while循环应该在调用perform的代码中。 - Adam Burley
@AdamBurley:请注意答案中第一步中的 uiController.loopMainThreadForAtLeast(50) 调用。这确保了在主线程上不会持续进行 view.visibility == View.GONE 检查,而只是每 50 毫秒检查一次。如果您认为 50 毫秒太短,可以将此值增加到您认为最佳的任何值。 - Adil Hussain

2
这个问题已经在这里解答了。
您可以通过使用Espresso为您的 Web 服务注册 IdlingResource 来处理此情况。请参阅这篇文章
很可能,您需要使用 CountingIdlingResource(它使用简单的计数器来跟踪何时处于空闲状态)。此示例测试演示了如何完成此操作。

1
这是我处理这种情况的方法:

public void waitForViewToDisappear(int viewId, long maxWaitingTimeMs) {
    long endTime = System.currentTimeMillis() + maxWaitingTimeMs;
    while (System.currentTimeMillis() <= endTime) {
        try {
            onView(allOf(withId(viewId), isDisplayed())).matches(not(doesNotExist()));
        } catch (NoMatchingViewException ex) {
            return; // view has disappeared
        }
    }
    throw new RuntimeException("timeout exceeded"); // or whatever exception you want
}

注意:matches(not(doesNotExist())) 是一种“空操作”匹配器;它只是确保 onView 部分实际运行。你同样可以编写一个什么都不做的 ViewAction 并将其封装在 perform 调用中,但这会增加更多代码行数,因此我选择了这种方式。

这对我完美地起作用了,谢谢Adam。 - toolmania1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接