web-dev-qa-db-fra.com

Comment affirmer à l'intérieur d'un RecyclerView en Espresso?

J'utilise espresso-contrib pour effectuer des actions sur un RecyclerView, et cela fonctionne comme il se doit, ex:

onView(withId(R.id.recycler_view))
.perform(RecyclerViewActions.actionOnItemAtPosition(0, click())); //click on first item

et j'ai besoin de faire des affirmations dessus. Quelque chose comme ça:

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

mais, parce que RecyclerViewActions est, bien sûr, en attente d'une action, il indique un deuxième type d'argument incorrect. Il n'y a pas de RecyclerViewAssertions sur espresso-contrib.

Existe-t-il un moyen d'effectuer des assertions sur une vue de recycleur?

43
juhlila

Vous devriez vérifier la solution de Danny Roa Actions RecyclerView personnalisées Et l'utiliser comme ceci:

onView(withRecyclerView(R.id.recycler_view)
    .atPositionOnView(1, R.id.ofElementYouWantToCheck))
    .check(matches(withText("Test text")));
48
HowieH

Plutôt facile. Aucune bibliothèque supplémentaire n'est nécessaire. Faire:

    onView(withId(R.id.recycler_view))
            .check(matches(atPosition(0, withText("Test Text"))));

si votre ViewHolder utilise ViewGroup, enveloppez withText() avec un hasDescendant() comme:

onView(withId(R.id.recycler_view))
                .check(matches(atPosition(0, hasDescendant(withText("Test Text")))));

avec la méthode que vous pouvez mettre dans votre classe 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);
        }
    };
}

Si votre article peut ne pas être visible à l'écran au début, faites-le défiler avant:

    onView(withId(R.id.recycler_view))
            .perform(scrollToPosition(87))
            .check(matches(atPosition(87, withText("Test Text"))));
80
riwnodennyk

Juste pour améliorer la réponse de riwnodennyk, si votre ViewHolder est un ViewGroup au lieu d'un objet direct View comme TextView, alors vous pouvez ajouter hasDescendant matcher pour faire correspondre l'objet TextView dans le ViewGroup. Exemple:

onView(withId(R.id.recycler_view))
    .check(matches(atPosition(0, hasDescendant(withText("First Element")))));
24
You Qi

Dans mon cas, il a fallu fusionner la solution de Danny Roa et de riwnodennyk:

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

et la méthode:

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);
        }
    };
}
5
Vince

J'ai eu du mal sur le même problème où j'ai une vue de recycleur avec 6 articles et ensuite je dois choisir celui en position 5 et le 5ème n'est pas visible sur la vue.
La solution ci-dessus fonctionne également. De plus, je sais que c'est un peu tard mais j'ai pensé que ça valait le coup d'être partagé.

Je l'ai fait avec une solution simple comme ci-dessous:

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

RecyclerViewActions.actionOnItem - Effectue un ViewAction sur une vue correspondant à viewHolderMatcher.

  1. Faites défiler RecyclerView jusqu'à la vue correspondant à itemViewMatcher
  2. Effectuer une action sur la vue correspondante

Plus de détails peuvent être trouvés à: RecyclerViewActions.actionOnItem

5
Hiten Bahri

La solution de Danny Roa est géniale, mais elle n'a pas fonctionné pour les éléments hors écran que je venais de parcourir. Le correctif remplace ceci:

View targetView = recyclerView.getChildAt(this.position)
                              .findViewById(this.viewId);

avec ça:

View targetView = recyclerView.findViewHolderForAdapterPosition(this.position)
                              .itemView.findViewById(this.viewId);

... dans la classe TestUtils .

1
I_AM_PANDA

Ce fil est assez ancien mais j'ai récemment étendu la prise en charge de RecyclerViewAction pour qu'elle soit également utilisable pour les assertions. Cela vous permet de tester des éléments hors écran sans spécifier de position tout en utilisant des égaliseurs de vue.

Consultez cet article> Mieux Android Test RecyclerView !

1
Max Buster