Cómo hacer clic en un elemento dentro de RecyclerView en Espresso

Tengo un RecyclerView (R.id.recyclerView) donde cada fila tiene una imagen (R.id.row_image) y un TextView. Quiero hacer clic en la imagen en la primera fila.
Intenté usar onData (..) pero parece que no funciona.

Use RecyclerViewActions

onView(withId(R.id.recyclerView)) .perform(actionOnItemAtPosition(0, click())); 

Incluye esto en tu script de gradle:

 dependencies { androidTestCompile 'com.android.support.test:testing-support-lib:0.1' androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0' androidTestCompile 'com.android.support.test.espresso:espresso-contrib:2.0' } 

Solo para agregar a la respuesta de Gabor (que es la respuesta correcta y completa desde Espresso 2.0).

Puede encontrar problemas en el momento de usar espresso-contrib y RecyclerView s (consulte el ticket de android-test-kit ).

Una solución consiste en agregar esta exclusión en la dependencia de espresso-contrib Gabor mencionó anteriormente:

 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' } 

(Esta es una respuesta en lugar de un comentario a la respuesta de Gabor porque todavía no tengo derecho a publicar comentarios)

Editar:

Espresso 2.0 ha sido lanzado, el registro de cambios incluye lo siguiente:

Nuevas características

  • espresso-contrib
    • RecyclerViewActions: maneja las interacciones con RecyclerViews

Vieja respuesta

Todavía no lo he probado yo mismo, pero Thomas Keller publicó esto en G + con una breve explicación y un enlace a un Gist con los correctores de vistas necesarios.

Dado que la nueva API de RecyclerView hereda de ViewGroup y no de AdapterView , no puede usar onData() Espresso para probar diseños utilizando este componente.

Enlace a Gist .

Adjuntaré el código, solo para completar (nota: ¡ no mío! Todo el crédito es para Thomas Keller)

ViewMatcher:

 public class ViewMatchers { @SuppressWarnings("unchecked") public static Matcher withRecyclerView(@IdRes int viewId) { return allOf(isAssignableFrom(RecyclerView.class), withId(viewId)); } @SuppressWarnings("unchecked") public static ViewInteraction onRecyclerItemView(@IdRes int identifyingView, Matcher identifyingMatcher, Matcher childMatcher) { Matcher itemView = allOf(withParent(withRecyclerView(R.id.start_grid)), withChild(allOf(withId(identifyingView), identifyingMatcher))); return Espresso.onView(allOf(isDescendantOfA(itemView), childMatcher)); } } 

Y uso de muestra:

 onRecyclerItemView(R.id.item_title, withText("Test"), withId(R.id.item_content)) .matches(check(withText("Test Content"))); 

Deberías usar una vista de acción personalizada:

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

He encontrado dos formas:

  1. Suponiendo que tiene una vista de texto con id “R.id.description” para cada elemento en RecyclerView. Puedes hacer esto para unir a un niño específico:

onView(allOf(withId(R.id.place_description),withText("what"))).perform(click());

  1. Un tutorial de Android Testing Codelab https://codelabs.developers.google.com/codelabs/android-testing/#0

`

 public Matcher withItemText(final String itemText) { checkArgument(!TextUtils.isEmpty(itemText),"cannot be null"); return new TypeSafeMatcher() { @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); } }; } 

`

Y luego, haz esto:

 onView(withItemText("what")).perform(click()); 

No necesita agregar “testing-support-lib”, ni “espresso: espresso-core”. Se agregan transitivos cuando se agrega “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' } 

Uso :

 onView(withId(R.id.recyclerView)).perform( RecyclerViewActions.actionOnItemAtPosition(0, click())); 

Seguí la respuesta de @Gabor, pero cuando incluí las bibliotecas, ¡ alcancé el límite de dex!

Entonces, getInstrumentation().waitForIdleSync(); las bibliotecas, agregué este getInstrumentation().waitForIdleSync(); y luego llamé a onView(withId...))...

Funciona perfectamente.

En su caso, tendrá múltiples vistas de imágenes con la misma ID, por lo que tendrá que averiguar algo sobre cómo puede seleccionar el elemento de la lista en particular.

Como publiqué aquí , puedes implementar tu marcador RecyclerView personalizado. Supongamos que tiene RecyclerView donde cada elemento tiene un tema que no concuerda:

 public static Matcher withItemSubject(final String subject) { Checks.checkNotNull(subject); return new BoundedMatcher( 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); } }; } 

Y el uso:

 onView(withId(R.id.my_recycler_view_id) .perform(RecyclerViewActions.actionOnHolderItem(withItemSubject("My subject"), click())); 

Básicamente puedes hacer coincidir todo lo que quieras. En este ejemplo usamos TextView pero puede ser cualquier elemento dentro del elemento RecyclerView .

Una cosa más para aclarar es verificar la visibilidad (subjectTextView.getVisibility() == View.VISIBLE) . Necesitamos tenerlo porque a veces otras vistas dentro de RecyclerView pueden tener el mismo tema pero sería con View.GONE . De esta forma, evitamos las coincidencias múltiples de nuestro marcador personalizado y solo el elemento objective que realmente muestra nuestro tema.