如何在没有 IdlingResource 的情况下在 Espresso 中等待异步任务
Posted
技术标签:
【中文标题】如何在没有 IdlingResource 的情况下在 Espresso 中等待异步任务【英文标题】:How to wait for async task in Espresso without IdlingResource 【发布时间】:2020-01-10 21:15:41 【问题描述】:我有以下浓缩咖啡代码:
@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 是不可能的。
感谢任何帮助!
【问题讨论】:
【参考方案1】:您仍然可以在不接触生产代码的情况下实现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(...)
可能是您的下一个选择,但它会不稳定或效率低下。
【讨论】:
【参考方案2】:使用空闲资源不需要更新生产中的代码。
但这也可以按照您的要求在没有空闲资源的情况下完成,您可以使用此自定义 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 毫秒)的超时时间。
【讨论】:
以上是关于如何在没有 IdlingResource 的情况下在 Espresso 中等待异步任务的主要内容,如果未能解决你的问题,请参考以下文章
java Android - 如何使用IdlingResource等待JUnit断言的异步操作(可能是Observables或EvenBus)。
java Android - 如何使用IdlingResource等待JUnit断言的异步操作(可能是Observables或EvenBus)。
java Android - 如何使用IdlingResource等待JUnit断言的异步操作(可能是Observables或EvenBus)。
java Android - 如何使用IdlingResource等待JUnit断言的异步操作(可能是Observables或EvenBus)。
java Android - 如何使用IdlingResource等待JUnit断言的异步操作(可能是Observables或EvenBus)。
java Android - 如何使用IdlingResource等待JUnit断言的异步操作(可能是Observables或EvenBus)。