[英]Android Espresso testing SwipeRefreshLayout OnRefresh not been triggered on swipeDown
作为集成测试的一部分,我正在尝试为 pull to refresh 编写简单的测试。 我正在使用最新的 androidX 测试组件和 Robolectric。 我正在测试一个孤立的片段,其中我正在注入模拟演示者。
XML 布局部分
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/refreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerTasks"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
片段部分
binding.refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
presenter.onRefresh();
}
});
测试:
onView(withId(R.id.refreshLayout)).perform(swipeDown());
verify(presenter).onRefresh();
但测试未通过,消息:
需要但未调用:presenter.onRefresh();
该应用程序运行良好,并拉动刷新调用presenter.onRefresh()。 我还调试了测试并调用了 setOnRefreshListener 并且它不是空值。 如果我使用自定义匹配器进行测试以检查 SwipeRefreshLayout 测试通过的状态。
onView(withId(R.id.refreshLayout)).check(matches(isRefreshing()));
上周末我做了一些小调查,因为我遇到了同样的问题,这让我很困扰。 我还与设备上发生的情况进行了一些比较以发现差异。
在androidx.swiperefreshlayout.widget.SwipeRefreshLayout
内部有一个mRefreshListener
,它将在onAnimationEnd
被调用时运行。 AnimationEnd
将触发OnRefreshListener.onRefresh
方法。
该动画侦听器 ( mRefreshListener
) 被传递给mCircleView (CircleImageView)
并调用圆动画开始。
在设备上,当调用视图draw
方法时,它将调用applyLegacyAnimation
方法,该方法将依次调用AnimationStart
方法。 在AnimationEnd,
将调用onRefresh
方法。
在 Robolectric 上, View
的 draw 方法永远不会被调用,因为这些项目实际上并没有被绘制。 这意味着动画永远不会运行,因此onRefresh
方法也不会运行。
我的结论是,由于实施限制,无法使用当前版本的 Robolectric 验证onRefresh
调用。 似乎计划在未来进行逼真的渲染。
我终于能够使用一种骇人听闻的方式解决这个问题:
fun swipeToRefresh(): ViewAction {
return object : ViewAction {
override fun getConstraints(): Matcher<View>? {
return object : BaseMatcher<View>() {
override fun matches(item: Any): Boolean {
return isA(SwipeRefreshLayout::class.java).matches(item)
}
override fun describeMismatch(item: Any, mismatchDescription: Description) {
mismatchDescription.appendText(
"Expected SwipeRefreshLayout or its Descendant, but got other View"
)
}
override fun describeTo(description: Description) {
description.appendText(
"Action SwipeToRefresh to view SwipeRefreshLayout or its descendant"
)
}
}
}
override fun getDescription(): String {
return "Perform swipeToRefresh on the SwipeRefreshLayout"
}
override fun perform(uiController: UiController, view: View) {
val swipeRefreshLayout = view as SwipeRefreshLayout
swipeRefreshLayout.run {
isRefreshing = true
// set mNotify to true
val notify = SwipeRefreshLayout::class.memberProperties.find {
it.name == "mNotify"
}
notify?.isAccessible = true
if (notify is KMutableProperty<*>) {
notify.setter.call(this, true)
}
// mockk mRefreshListener onAnimationEnd
val refreshListener = SwipeRefreshLayout::class.memberProperties.find {
it.name == "mRefreshListener"
}
refreshListener?.isAccessible = true
val animatorListener = refreshListener?.get(this) as Animation.AnimationListener
animatorListener.onAnimationEnd(mockk())
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.