繁体   English   中英

Android Espresso 测试 SwipeRefreshLayout OnRefresh 未在 swipeDown 上触发

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM