如何在没有空闲资源的情况下等待浓缩咖啡中的异步任务



我有以下浓缩咖啡代码:

@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 秒内返回响应,然后隐藏的文本视图将变得可见。

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

我无法接触生产代码,因此无法在生产代码中实现 IdlingResources。

任何帮助不胜感激!

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

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()))

如果没有IdlingResourceThread.sleep(...)可能是您的下一个选择,但它将是片状或低效的。

使用空闲资源不需要在生产中更新代码。

但这也可以在不按照您的要求闲置资源的情况下完成,您可以使用此自定义 ViewAction:

/**
* Perform action of waiting until the element is accessible & not shown.
* @param viewId The id of the view to wait for.
* @param millis The timeout of until when to wait for.
*/
public static ViewAction waitUntilShown(final int viewId, final long millis) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return isRoot();
}
@Override
public String getDescription() {
return "wait for a specific view with id <" + viewId + "> is shown during " + millis + " millis.";
}
@Override
public void perform(final UiController uiController, final View view) {
uiController.loopMainThreadUntilIdle();
final long startTime = System.currentTimeMillis();
final long endTime = startTime + millis;
final Matcher<View> viewMatcher = withId(viewId);
do {
for (View child : TreeIterables.breadthFirstViewTraversal(view)) {
// found view with required ID
if (viewMatcher.matches(child) && child.isShown()) {
return;
}
}
uiController.loopMainThreadForAtLeast(50);
}
while (System.currentTimeMillis() < endTime);
// timeout happens
throw new PerformException.Builder()
.withActionDescription(this.getDescription())
.withViewDescription(HumanReadables.describe(view))
.withCause(new TimeoutException())
.build();
}
};
}

你可以这样使用它:

onView(isRoot()).perform(waitUntilShown(R.id.hiddenTextView, 5000));

如有必要,更新 5 秒(5000 毫秒(的超时。

最新更新