How to wait for async task in Espresso without IdlingResource
我有以下浓缩咖啡代码:
1 2 3 4 5 6 7 | @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 是不可能的。
感谢任何帮助!
使用空闲资源不需要更新生产中的代码。
但这也可以按照您的要求在没有空闲资源的情况下完成,您可以使用此自定义 ViewAction:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | /** * 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(); } }; } |
你可以这样使用它:
1 | onView(isRoot()).perform(waitUntilShown(R.id.hiddenTextView, 5000)); |
如有必要,更新 5 秒(5000 毫秒)的超时时间。
你仍然可以实现
的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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() } } |
然后创建一个自定义的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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) } } } } } |
并更新您的测试以使用该操作:
1 2 3 4 | onView(withId(R.id.hiddenTextView)).perform(waitUntil(isDisplayed())) // or onView(withId(R.id.hiddenTextView)).perform(waitUntil(withEffectiveVisibility(VISIBLE))) .check(matches(isDisplayed())) |
如果没有