Подтвердить что ты не робот

Как нажать на элемент внутри RecyclerView в Espresso

У меня есть RecyclerView (R.id.recyclerView), где каждая строка имеет изображение (R.id.row_image) и TextView. Я хочу щелкнуть по изображению в первой строке.
Я попытался использовать onData (..), но он не работает.

4b9b3361

Ответ 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'
}

Ответ 2

Просто добавьте ответ Gabor (это правильный и полный ответ с Espresso 2.0).

У вас могут возникнуть проблемы в момент использования espresso-contrib и RecyclerView (см. андроид-тест-билет).

Обходным путем является добавление этого исключения в зависимость 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, потому что я не имею права оставлять комментарии еще)

Ответ 3

Изменить:

Эспрессо 2.0 был выпущен, журнал изменений включает в себя следующее:

Новые функции

  • Эспрессо-вно
    • RecyclerViewActions: обрабатывает взаимодействия с RecyclerViews

Старый ответ

Я еще не проверял это сам, но Томас Келлер опубликовал это на G+ с кратким объяснением и ссылкой на Gist с необходимыми соответствиями представления.

Поскольку новый API RecyclerView наследуется от ViewGroup, а не от AdapterView, вы не можете использовать Espresso onData() для тестирования макетов с использованием этого компонента.

Ссылка на Gist.

Я приложу код, просто для полноты (примечание: не мое! Все заслуги достаются Томасу Келлеру)

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

Вы должны использовать Custom 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

Вам не нужно добавлять "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()));

Ответ 6

Я нашел два пути:

  • Предполагая, что у вас есть текстовое представление с идентификатором "R.id.description" для каждого элемента в RecyclerView. Вы можете сделать это, чтобы соответствовать конкретному ребенку:

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());

Ответ 7

Я последовал за ответом @Gabor, но когда я включил библиотеки, я ударил лимит dex!

Итак, я удалил библиотеки, добавил этот getInstrumentation().waitForIdleSync();, а затем просто вызвал onView(withId...))...

Работает отлично.

В вашем случае у вас будет несколько изображений с одинаковым идентификатором, поэтому вам нужно будет выяснить, как вы можете выбрать конкретный элемент списка.

Ответ 8

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