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

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


person Jacki    schedule 10.12.2014    source источник


Ответы (10)


Используйте 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'
}
person Gabor    schedule 27.12.2014
comment
Я получаю сообщение о том, что запуск теста завершился неудачно: запуск инструментария завершился неудачно из-за «java.lang.ClassNotFoundException», когда я добавляю espresso-contrib. - person dannyroa; 24.03.2015
comment
@dannyroa, вы должны исключить из него группу: 'javax.inject' - person midnight; 13.04.2015
comment
Итак, как дела check(isDisplayed()) ? - person Christopher Francisco; 03.02.2016
comment
для людей, имеющих проблему @dannyroa, ознакомьтесь с ответом jdebon ниже - person Gabor; 03.02.2016
comment
Новые зависимости AndroidX описаны здесь developer.android.com/training/testing/set- аппроект - person Michael Osofsky; 31.01.2019
comment
зависимость androidTestCompile 'com.android.support.test:testing-support-lib:0.1' не требуется для выполнения щелчка в RV, что может вызвать проблемы при компиляции кода Kotlin. - person Ângelo Polotto; 26.04.2019
comment
developer.android.com/training/testing/espresso/ см. здесь для более подробной информации - person Anmol; 13.10.2019

Просто чтобы добавить к ответу Габора (это правильный и полный ответ, начиная с Espresso 2.0).

Вы можете столкнуться с проблемами при использовании espresso-contrib и RecyclerView (см. android-test- комплектный билет).

Обходной путь — добавить это исключение в упомянутую выше зависимость 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'
}

(Это ответ вместо комментария к ответу Габора, потому что у меня пока нет права оставлять комментарии)

person Sir4ur0n    schedule 19.06.2015
comment
это помогло исправить java.lang.NoClassDefFoundError, спасибо! - person nommer; 01.09.2015

Редактировать:

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

Новые особенности

  • espresso-contrib
    • RecyclerViewActions: handles interactions with RecyclerViews

Старый ответ

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

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

Ссылка на Gist.

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

Вьюматчер:

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")));
person aried3r    schedule 15.12.2014

Вы должны использовать 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));
    }
}
person fede1608    schedule 27.01.2016
comment
Вау, брат, ты спас мой день, братан. - person Nilesh Panchal; 31.01.2019

Я нашел два способа:

  1. Предполагая, что у вас есть текстовое представление с идентификатором «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());
person Luo Lei    schedule 22.01.2016
comment
Первый способ для меня лучший, т.к. он не требует эспрессо-вклада, который ломал сборку моих приложений. - person Artem Mostyaev; 30.03.2016

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

Используйте последнюю версию эспрессо по этой ссылке.

person Andrew Emad    schedule 15.10.2020

Вам не нужно добавлять «testing-support-lib» или «espresso:espresso-core». Они добавляются транзитивно, когда добавляется «espresso:espresso-contrib».

класс сборки

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()));
person Víctor Albertos    schedule 03.09.2015

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

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

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

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

person vedant    schedule 25.03.2016

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

person denys    schedule 04.11.2016

С помощью этого кода вы можете прокручивать представление переработчика, чтобы найти свой элемент с текстом и выполнить над ним щелчок или другое действие.

зависимости

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()));
}
person without_margin    schedule 12.11.2020