【问题标题】:How to click on an item inside a RecyclerView in Espresso如何在 Espresso 中单击 RecyclerView 内的项目
【发布时间】:2014-12-10 08:45:05
【问题描述】:

我有一个 RecyclerView (R.id.recyclerView),其中每一行都有一个图像 (R.id.row_image) 和一个 TextView。我想点击第一行的图片。
我尝试使用 onData(..) 但它似乎不起作用。

【问题讨论】:

    标签: android android-recyclerview android-espresso


    【解决方案1】:

    使用RecyclerViewActions

    onView(withId(R.id.recyclerView))
        .perform(actionOnItemAtPosition(0, click()));
    

    将其包含在您的 gradle 脚本中:

    dependencies {
        androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
        androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.0'
    }
    

    【讨论】:

    • 我得到测试运行失败:当我添加 espresso-contrib 时,仪器运行因“java.lang.ClassNotFoundException”而失败。
    • @dannyroa,你应该排除 group: 'javax.inject' 就可以了
    • 那么,你怎么check(isDisplayed())
    • 对于遇到@dannyroa 问题的人,请查看下面 jdebon 的答案
    • 这里描述了新的 AndroidX 依赖项developer.android.com/training/testing/set-up-project
    【解决方案2】:

    只是添加到 Gabor 的答案(这是自 Espresso 2.0 以来正确且完整的答案)。

    您在使用espresso-contribRecyclerViews 时可能会遇到问题(请参阅android-test-kit ticket)。

    一种解决方法是在上面提到的 espresso-contrib 依赖项 Gabor 中添加此排除项:

    androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.0') {
        exclude group: 'com.android.support', module: 'appcompat'
        exclude group: 'com.android.support', module: 'support-v4'
        exclude module: 'recyclerview-v7'
    }
    

    (这是一个答案,而不是对 Gabor 答案的评论,因为我还无权发布 cmets)

    【讨论】:

    • 这有助于修复 java.lang.NoClassDefFoundError,谢谢!
    【解决方案3】:

    编辑:

    Espresso 2.0 已经发布,the changelog 包括以下内容:

    新功能

    • 浓缩咖啡贡献
      • RecyclerViewActions:处理与 RecyclerViews 的交互

    旧答案

    我自己还没有对此进行测试,但是 G+ 上的 Thomas Keller posted this 提供了简短的解释和指向带有必要视图匹配器的 Gist 的链接。

    由于新的 RecyclerView API 继承自 ViewGroup 而不是 AdapterView,因此您不能使用 Espresso 的 onData() 来测试使用此组件的布局。

    Link to Gist.

    为了完整起见,我将附上代码(注意:不是我的!所有功劳归于 Thomas Keller)

    ViewMatcher:

    public class ViewMatchers {
        @SuppressWarnings("unchecked")
        public static Matcher<View> withRecyclerView(@IdRes int viewId) {
            return allOf(isAssignableFrom(RecyclerView.class), withId(viewId));
        }
    
        @SuppressWarnings("unchecked")
        public static ViewInteraction onRecyclerItemView(@IdRes int identifyingView, Matcher<View> identifyingMatcher, Matcher<View> childMatcher) {
            Matcher<View> itemView = allOf(withParent(withRecyclerView(R.id.start_grid)),
                    withChild(allOf(withId(identifyingView), identifyingMatcher)));
            return Espresso.onView(allOf(isDescendantOfA(itemView), childMatcher));
        }
    }
    

    及示例用法:

    onRecyclerItemView(R.id.item_title, withText("Test"),  withId(R.id.item_content))
        .matches(check(withText("Test Content")));
    

    【讨论】:

      【解决方案4】:

      您应该使用自定义 ViewAction:

      public void clickOnImageViewAtRow(int position) {
          onView(withId(R.id.recycler_view)).perform(RecyclerViewActions.actionOnItemAtPosition(position, new ClickOnImageView()));
      }
      
      public class ClickOnImageView implements ViewAction{
          ViewAction click = click();
      
          @Override
          public Matcher<View> getConstraints() {
              return click.getConstraints();
          }
      
          @Override
          public String getDescription() {
              return " click on custom image view";
          }
      
          @Override
          public void perform(UiController uiController, View view) {
              click.perform(uiController, view.findViewById(R.id.imageView));
          }
      }
      

      【讨论】:

      • 哇,兄弟,你拯救了我的一天。
      【解决方案5】:

      与上面相同的答案,但是由于您必须提供 Viewholder 类型而进行了一些小修改

      使用 RecyclerViewActions

      fun tapOnRecyclerView(@IdRes resId: Int , position: Int) = onView(withId(resId))
              .perform(actionOnItemAtPosition<RecyclerView.ViewHolder>(position, click()));
      

      要将这个库包含在您的项目中,请使用它

      androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
      androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion"
      

      更喜欢使用来自link 的最新 espresso 版本

      【讨论】:

        【解决方案6】:

        我找到了两种方法:

        1. 假设 RecyclerView 中的每个项目都有一个 ID 为“R.id.description”的文本视图。您可以这样做来匹配特定的孩子:

        onView(allOf(withId(R.id.place_description),withText("what"))).perform(click());

        1. Android Testing Codelab 教程 https://codelabs.developers.google.com/codelabs/android-testing/#0

        `

        public Matcher<View> withItemText(final String itemText) {
                checkArgument(!TextUtils.isEmpty(itemText),"cannot be null");
                return new TypeSafeMatcher<View>() {
                    @Override
                    protected boolean matchesSafely(View item) {
                        return allOf(isDescendantOfA(isAssignableFrom(RecyclerView.class)),withText(itemText)).matches(item);
                    }
        
                    @Override
                    public void describeTo(Description description) {
                        description.appendText("is descendant of a RecyclerView with text" + itemText);
                    }
                };
            }
        

        `

        然后,这样做:

        onView(withItemText("what")).perform(click());
        

        【讨论】:

        • 第一种方法对我来说是最好的,因为它不需要 espresso-contrib,这会破坏我的应用程序的构建。
        【解决方案7】:

        您不需要添加“testing-support-lib”,也不需要添加“espresso:espresso-core”。当添加“espresso:espresso-contrib”时,它们是可传递的。

        build.grade

        dependencies {
            androidTestCompile 'com.android.support.test:runner:0.3'
            androidTestCompile 'com.android.support.test:rules:0.3'
            androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.2'
        }
        

        用法

        onView(withId(R.id.recyclerView)).perform(
                    RecyclerViewActions.actionOnItemAtPosition(0, click()));
        

        【讨论】:

          【解决方案8】:

          我遵循了@Gabor 的回答,但是当我包含库时,我达到了 dex 限制!

          所以,我删除了这些库,添加了这个 getInstrumentation().waitForIdleSync();,然后只调用了 onView(withId...))...

          完美运行。

          在您的情况下,您将拥有多个具有相同 ID 的图像视图,因此您必须弄清楚如何选择特定列表项。

          【讨论】:

            【解决方案9】:

            正如我发布的here,您可以实现您的自定义RecyclerView 匹配器。假设您有RecyclerView,其中每个元素都有您想匹配的主题:

            public static Matcher<RecyclerView.ViewHolder> withItemSubject(final String subject) {
                Checks.checkNotNull(subject);
                return new BoundedMatcher<RecyclerView.ViewHolder, MyCustomViewHolder>(
                        MyCustomViewHolder.class) {
            
                    @Override
                    protected boolean matchesSafely(MyCustomViewHolder viewHolder) {
                        TextView subjectTextView = (TextView)viewHolder.itemView.findViewById(R.id.subject_text_view_id);
            
                        return ((subject.equals(subjectTextView.getText().toString())
                                && (subjectTextView.getVisibility() == View.VISIBLE)));
                    }
            
                    @Override
                    public void describeTo(Description description) {
                        description.appendText("item with subject: " + subject);
                    }
                };
            }
            

            及用法:

            onView(withId(R.id.my_recycler_view_id)
                .perform(RecyclerViewActions.actionOnHolderItem(withItemSubject("My subject"), click()));
            

            基本上你可以匹配任何你想要的东西。在此示例中,我们使用了主题 TextView,但它可以是 RecyclerView 项目内的任何元素。

            要澄清的另一件事是检查可见性(subjectTextView.getVisibility() == View.VISIBLE)。我们需要它,因为有时RecyclerView 中的其他视图可以具有相同的主题,但它会与View.GONE 一起使用。通过这种方式,我们避免了自定义匹配器的多次匹配,并且只针对实际显示我们主题的项目。

            【讨论】:

              【解决方案10】:

              使用此代码,您可以滚动回收站视图以查找带有文本的项目并对其执行单击或其他操作。

              依赖项

              androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
              androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.3.0'
              

              代码

              @Test
              public void scrollRecyclerViewAndClick() {
                  onView(withId(R.id.recycler_view)).
                          perform(RecyclerViewActions.
                          actionOnItem(withText("specific string"), click()));
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-04-13
                • 1970-01-01
                • 2019-12-01
                • 1970-01-01
                • 2020-07-17
                • 1970-01-01
                相关资源
                最近更新 更多