У меня есть RecyclerView (R.id.recyclerView), где каждая строка имеет изображение (R.id.row_image) и TextView. Я хочу щелкнуть по изображению в первой строке.
Я попытался использовать onData (..), но он не работает.
Как нажать на элемент внутри RecyclerView в Espresso
Ответ 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
, вы не можете использовать EspressoonData()
для тестирования макетов с использованием этого компонента.
Я приложу код, просто для полноты (примечание: не мое! Все заслуги достаются Томасу Келлеру)
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());
- Учебник от 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
. Таким образом, мы избегаем нескольких совпадений нашего пользовательского совпадения и объекта цели, который фактически отображает наш объект.