У меня есть RecyclerView (R.id.recyclerView), где каждая строка имеет изображение (R.id.row_image) и TextView. Я хочу щелкнуть изображение в первой строке.
Я пытался использовать onData(..), но это не сработало.
Как щелкнуть элемент внутри RecyclerView в Espresso
Ответы (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'
}
check(isDisplayed())
?
- person Christopher Francisco; 03.02.2016
Просто чтобы добавить к ответу Габора (это правильный и полный ответ, начиная с 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'
}
(Это ответ вместо комментария к ответу Габора, потому что у меня пока нет права оставлять комментарии)
Редактировать:
Espresso 2.0 был выпущен, журнал изменений включает следующий:
Новые особенности
- espresso-contrib
- RecyclerViewActions: handles interactions with RecyclerViews
Старый ответ
Я еще не тестировал это сам, но Томас Келлер опубликовал это в Google+ с краткое объяснение и ссылка на Gist с необходимыми сопоставителями представлений.
Поскольку новый API
RecyclerView
наследуется отViewGroup
, а не отAdapterView
, вы не можете использоватьonData()
Espresso для тестирования макетов с использованием этого компонента.
Я прикреплю код просто для полноты картины (примечание: не мой! Все заслуги принадлежат Томасу Келлеру)
Вьюматчер:
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")));
Вы должны использовать 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));
}
}
Я нашел два способа:
- Предполагая, что у вас есть текстовое представление с идентификатором «R.id.description» для каждого элемента в RecyclerView. Вы можете сделать это, чтобы соответствовать конкретному ребенку:
onView(allOf(withId(R.id.place_description),withText("what"))).perform(click());
- Учебник от 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());
Тот же ответ, что и выше, но при небольшой модификации, поскольку вы должны указать тип 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"
Используйте последнюю версию эспрессо по этой ссылке.
Вам не нужно добавлять «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()));
Я последовал ответу @Gabor, но когда я включил библиотеки, я достиг предела dex!
Итак, я удалил библиотеки, добавил этот getInstrumentation().waitForIdleSync();
, а затем просто позвонил onView(withId...))...
Работает отлично.
В вашем случае у вас будет несколько просмотров изображений с одним и тем же идентификатором, поэтому вам нужно будет что-то выяснить, как выбрать конкретный элемент списка.
Как я разместил здесь, вы можете реализовать свой собственный 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
. Таким образом, мы избегаем множественных совпадений нашего пользовательского сопоставления и нацеливаемся только на элемент, который действительно отображает нашу тему.
С помощью этого кода вы можете прокручивать представление переработчика, чтобы найти свой элемент с текстом и выполнить над ним щелчок или другое действие.
зависимости
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()));
}