如何在Espresso中的RecyclerView内断言?

juh*_*ila 46 java android android-espresso

我正在使用espresso-contrib来对a执行操作RecyclerView,并且它可以正常工作,例如:

onView(withId(R.id.recycler_view))
.perform(RecyclerViewActions.actionOnItemAtPosition(0, click())); //click on first item
Run Code Online (Sandbox Code Playgroud)

我需要对它进行断言.像这样的东西:

onView(withId(R.id.recycler_view))
    .perform(RecyclerViewActions.actionOnItemAtPosition(0, check(matches(withText("Test Text"))));
Run Code Online (Sandbox Code Playgroud)

但是,因为RecyclerViewActions当然是期待一个动作,它会说错误的第二个参数类型.RecyclerViewAssertionsespresso-contrib 上没有.

有没有办法在回收站视图上执行断言?

riw*_*nyk 83

满容易.不需要额外的库.做:

    onView(withId(R.id.recycler_view))
            .check(matches(atPosition(0, withText("Test Text"))));
Run Code Online (Sandbox Code Playgroud)

如果您ViewHolder使用ViewGroup中,包装withText()hasDescendant(),如:

onView(withId(R.id.recycler_view))
                .check(matches(atPosition(0, hasDescendant(withText("Test Text")))));
Run Code Online (Sandbox Code Playgroud)

用你可以放入Utils课堂的方法.

public static Matcher<View> atPosition(final int position, @NonNull final Matcher<View> itemMatcher) {
    checkNotNull(itemMatcher);
    return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) {
        @Override
        public void describeTo(Description description) {
            description.appendText("has item at position " + position + ": ");
            itemMatcher.describeTo(description);
        }

        @Override
        protected boolean matchesSafely(final RecyclerView view) {
            RecyclerView.ViewHolder viewHolder = view.findViewHolderForAdapterPosition(position);
            if (viewHolder == null) {
                // has no item on such position
                return false;
            }
            return itemMatcher.matches(viewHolder.itemView);
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

如果您的项目最初在屏幕上可能不可见,请在之前滚动到该项目:

    onView(withId(R.id.recycler_view))
            .perform(scrollToPosition(87))
            .check(matches(atPosition(87, withText("Test Text"))));
Run Code Online (Sandbox Code Playgroud)


How*_*ieH 49

您应该查看Danny Roa的解决方案Custom RecyclerView Actions 并使用它如下:

onView(withRecyclerView(R.id.recycler_view)
    .atPositionOnView(1, R.id.ofElementYouWantToCheck))
    .check(matches(withText("Test text")));
Run Code Online (Sandbox Code Playgroud)

  • 尝试先滚动到该位置?onView(withId(R.id.id.recycler_view))执行(RecyclerViewActions.scrollTo(2)). (2认同)
  • 还是一样.如果位置= 30则不起作用.问题是`RecyclerView.findViewHolderForAdapterPosition(position)`应该用于`RecyclerView.getChildAt()`,如下面的答案所示. (2认同)
  • 如果项目不在屏幕上,我仍然不起作用,我将 findViewHolderForAdapterPosition(position) 添加到 RecyclerViewMatcher。如果我这样做: onView(withRecyclerView(R.id.synology_apps_rv).atPositionOnView(7, R.id.not_installed_tv)).check(matches(withText(R.string.notinstalled))); 测试失败,但如果我这样做: onView(withId(R.id.synology_apps_rv)).perform(RecyclerViewActions.scrollToPosition(7));onView(withRecyclerView(R.id.synology_apps_rv).atPositionOnView(7, R.id) .not_installed_tv)).check(matches(withText(R.string.notinstalled))); 测试成功 (2认同)

You*_* Qi 24

只是为了增强riwnodennyk的答案,如果你ViewHolder是一个ViewGroup而不是直接View对象TextView,那么你可以添加hasDescendant匹配器来匹配中的TextView对象ViewGroup.例:

onView(withId(R.id.recycler_view))
    .check(matches(atPosition(0, hasDescendant(withText("First Element")))));
Run Code Online (Sandbox Code Playgroud)


awa*_*qar 10

对于 Kotlin 用户:我知道我已经迟到了,而且我很困惑为什么人们必须自定义 RecycleView Actions。我一直想达到完全相同的预期结果:这是我的解决方案,希望它有效并帮助新人。也请对我宽容一点,因为我可能会被封锁。

onView(withId(R.id.recycler_view))
.perform(actionOnItemAtPosition<ViewHolder>(46, scrollTo()))
.check(matches(hasDescendant(withText("TextToMatch"))))
Run Code Online (Sandbox Code Playgroud)

//编辑于 18/12/21:在调查 Bjorn 的评论后,请参阅下面更好的方法:

onView(withId(R.id.recyclerView))
.perform(RecyclerViewActions.scrollToPosition<ViewHolder>(12))
.check(matches(hasDescendant(withText("TexttoMatch"))))
Run Code Online (Sandbox Code Playgroud)

  • 抱歉,但这不会检查给定索引的确切位置,而是检查整个 RecyclerView。只要用 47 或 45 来测试它,测试就会通过。 (3认同)
  • 编辑后的方法也有同样的问题。它不检查确切位置。 (2认同)

小智 6

以我为例,有必要合并Danny Roa和riwnodennyk的解决方案:

onView(withId(R.id.recyclerview))
.perform(RecyclerViewActions.scrollToPosition(80))
.check(matches(atPositionOnView(80, withText("Test Test"), R.id.targetview)));
Run Code Online (Sandbox Code Playgroud)

和方法:

public static Matcher<View> atPositionOnView(final int position, final Matcher<View> itemMatcher,
        @NonNull final int targetViewId) {

    return new BoundedMatcher<View, RecyclerView>(RecyclerView.class) {
        @Override
        public void describeTo(Description description) {
            description.appendText("has view id " + itemMatcher + " at position " + position);
        }

        @Override
        public boolean matchesSafely(final RecyclerView recyclerView) {
            RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(position);
            View targetView = viewHolder.itemView.findViewById(targetViewId);
            return itemMatcher.matches(targetView);
        }
    };
}
Run Code Online (Sandbox Code Playgroud)


小智 5

我一直在同一个问题上苦苦挣扎,因为我有一个包含6个项目的回收视图,然后我必须在位置5选择一个,而第5个在视图上不可见。
上述解决方案也有效。另外,我知道它有点晚,但认为值得分享。

我用一个简单的解决方案做到了这一点,如下所示:

onView(withId(R.id.recylcer_view))
    .perform(RecyclerViewActions.actionOnItem(
         hasDescendant(withText("Text of item you want to scroll to")), 
         click()));
Run Code Online (Sandbox Code Playgroud)

RecyclerViewActions.actionOnItem- ViewAction在匹配的视图上执行viewHolderMatcher

  1. 滚动RecyclerView到匹配的视图itemViewMatcher
  2. 对匹配的视图执行操作

可以在以下位置找到更多详细信息:RecyclerViewActions.actionOnItem

  • 谢谢!这适用于具有*简单*项目视图的`RecyclerView`。我有一个 ** 在视图层次结构中具有多级嵌套** 的项目。如果我使用你的解决方案,我最终会得到:`PerformException:错误执行'执行ViewAction:单击项目匹配:持有人有视图:有后代:有文本:是“测试显示名称”'在视图上'with id:com .example.com:id/my_recycler_view'。` (2认同)