Как разрешить Espresso щелкнуть конкретный элемент RecyclerView?

Я пытаюсь написать инструментарий с помощью Espresso для моего приложения для Android, которое использует RecyclerView, Вот основной макет:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/grid"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</android.support.design.widget.CoordinatorLayout>

... и макет представления элемента:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/label"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</RelativeLayout>

Это означает, что есть пара TextViews на экране которого все имеют label Я бы. Я пытаюсь нажать на определенный ярлык. Я попытался позволить тест-рекордеру Espresso сгенерировать код:

onView(allOf(withId(R.id.grid), isDisplayed()))
    .perform(actionOnItemAtPosition(5, click()));

Я скорее хочу что-то вроде:

// Not working
onView(allOf(withId(R.id.grid), isDisplayed()))
    .perform(actionOnItem(withText("Foobar"), click()));

Показания

Я прочитал немного о теме, но все еще не могу понять, как проверить RecyclerView элемент на основе его содержимого, а не на основе его индекса позиции.

2 ответа

Вы можете реализовать свой заказ 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, Таким образом, мы избегаем многократного совпадения нашего пользовательского сопоставления и целевого элемента, который фактически отображает нашу тему.

actionOnItem() совпадает с itemView ViewHolder. В этом случае TextView оборачивается в RelativeLayout, поэтому вам необходимо обновить Matcher для его учета.

onView(allOf(withId(R.id.grid), isDisplayed()))
    .perform(actionOnItem(withChild(withText("Foobar")), click()));

Вы также можете использовать hasDescendant(), чтобы обернуть ваш matcher в случае более сложной вложенности.

Другие вопросы по тегам