Cómo probar listas y adaptadores

1. Antes de comenzar

En codelabs anteriores, aprendiste a escribir y ejecutar pruebas de instrumentación y de unidades. En este codelab, se presentan algunas prácticas recomendadas cuando se escriben pruebas y se explica cómo agregar dependencias específicas de Gradle para pruebas. También podrás practicar cómo escribir más pruebas de instrumentación y de unidades.

Requisitos previos

  • Haber abierto un proyecto existente en Android Studio
  • Haber escrito pruebas de instrumentación y de unidades en Android Studio
  • Tener cierta experiencia en la navegación de proyectos en Android Studio
  • Tener cierta experiencia en el trabajo con archivos build.gradle en Android Studio.

Qué aprenderás

  • Los aspectos básicos para escribir una prueba
  • Cómo agregar dependencias de Gradle específicas de pruebas
  • Cómo probar listas con pruebas de instrumentación

Requisitos

  • Una computadora que tenga Android Studio instalado
  • El código de la solución de la app de Affirmations

Descarga el código de partida para este codelab

En este codelab, agregarás pruebas de instrumentación al código de solución para la app de Affirmations.

  1. Navega a la página de repositorio de GitHub del proyecto.
  2. Verifica que el nombre de la rama coincida con el especificado en el codelab. Por ejemplo, en la siguiente captura de pantalla, el nombre de la rama es main.

1e4c0d2c081a8fd2.png

  1. En la página de GitHub de este proyecto, haz clic en el botón Code, el cual abre una ventana emergente.

1debcf330fd04c7b.png

  1. En la ventana emergente, haz clic en el botón Download ZIP para guardar el proyecto en tu computadora. Espera a que se complete la descarga.
  2. Ubica el archivo en tu computadora (probablemente en la carpeta Descargas).
  3. Haz doble clic en el archivo ZIP para descomprimirlo. Se creará una carpeta nueva con los archivos del proyecto.

Abre el proyecto en Android Studio

  1. Inicia Android Studio.
  2. En la ventana Welcome to Android Studio, haz clic en Open.

d8e9dbdeafe9038a.png

Nota: Si Android Studio ya está abierto, selecciona la opción de menú File > Open.

8d1fda7396afe8e5.png

  1. En el navegador de archivos, ve hasta donde se encuentra la carpeta del proyecto descomprimida (probablemente en Descargas).
  2. Haz doble clic en la carpeta del proyecto.
  3. Espera a que Android Studio abra el proyecto.
  4. Haz clic en el botón Run 8de56cba7583251f.png para compilar y ejecutar la app. Asegúrate de que funcione como se espera.

2. Descripción general de la app de partida

La app de Affirmations consta de una pantalla que le muestra al usuario una lista de imágenes vinculadas con palabras de afirmación.

3. Prácticas recomendadas

Por diseño, el código de prueba se ve diferente de la lógica empresarial para una aplicación. Esto se debe a que las pruebas no deben contener lógica; solo deben probarla. Por lo tanto, las pruebas no deben tener sentencias condicionales, como if o when, ni declaraciones de flujo de control, como for o while. Tampoco deben manipular valores ni realizar cálculos reales.

Si bien es posible que las pruebas requieran algunas de estas situaciones, en general, debes evitarlas. Como es el tipo de lógica que queremos probar en nuestra app, si tuviéramos ese tipo de código en una prueba, podría fallar de la misma manera que lo haría el código de nuestra app.

Las pruebas de unidades solo deben llamar al fragmento de código de nuestra app que sea necesario para la prueba y probar los valores o el estado de ese código que resulta de la llamada al código. Las pruebas de UI solo deben hacer pruebas para obtener el estado esperado de la interfaz de usuario. Quizás te lleve un tiempo comprender este concepto, pero no te preocupes. Hay algunos temas que ayudan a explicarlo y que abordaremos en codelabs futuros. Mientras tanto, a medida que escribamos más pruebas, presta especial atención a los enfoques que utilizamos para escribirlas.

4. Cómo crear los directorios de prueba

En un codelab anterior, aprendiste a crear un directorio androidTest para pruebas de instrumentación. Repite ese proceso para este proyecto en los directorios androidTest y test. El proceso es el mismo para ambos, la única diferencia es que, para el directorio test, debes seleccionar test/java en el menú desplegable New Directory, en lugar de androidTest/java. Crea un paquete para cada directorio nuevo llamado com.example.affirmations.

d762ecd8950e97b2.png

5. Cómo crear una clase de prueba de instrumentación

Crea una clase nueva en androidTest -> com.example.affirmations llamada AffirmationsListTests.kt.

Al igual que con la app de Dice Roller, Affirmations solo tiene una actividad. Para probar la IU de la actividad, debemos especificar que queremos que se inicie. Intenta recordar cómo hacerlo por tu cuenta.

  1. Agrega un ejecutor de pruebas a la clase recién creada.
@RunWith(AndroidJUnit4::class)
  1. Crea una regla de situación de actividad para la actividad principal.
@get:Rule
val activity = ActivityScenarioRule(MainActivity::class.java)
  1. La app de Affirmations muestra una lista de imágenes y sus respectivas afirmaciones positivas. La IU no permite ninguna interacción con los elementos (como hacer clic o deslizar el dedo, por ejemplo). Por lo tanto, para esta app, la prueba de instrumentación solo prueba datos estáticos. Crea un método de prueba llamado scroll_to_item(). Recuerda que debe tener anotaciones con @Test.

Esta prueba debe desplazarse a un elemento específico de la lista. Aún no abordamos este enfoque, ya que requiere un método al que nuestro proyecto aún no tiene referencia. Antes de continuar con la prueba, debemos agregar dependencias de prueba.

6. Cómo agregar dependencias de prueba de instrumentación

Ya deberías conocer el proceso de agregar de dependencias de Gradle para usarlas en el código de tu app. Gradle también nos permite agregar dependencias específicamente para las pruebas de unidades y de instrumentación. Abre el archivo build.gradle de nivel de la app ubicado en app -> build.gradle. En la sección de dependencias, hay tres tipos de implementaciones para las dependencias: implementation, testImplementation y androidTestImplementation.

implementation es para las dependencias que se usarán en la aplicación, testImplementation es para las dependencias que se usarán en pruebas de unidades y androidTestImplementation es para las dependencias que se usarán en las pruebas de instrumentación.

  1. Agrega una dependencia para permitir la interacción con las pruebas de instrumentación de RecyclerView. Agrega la siguiente biblioteca como androidTestImplementation:
androidx.test.espresso:espresso-contrib:3.4.0

Las dependencias se verán de la siguiente manera:

dependencies {
    ...
    androidTestImplementation
'androidx.test.espresso:espresso-contrib:3.4.0'
}
  1. Ahora, sincroniza el proyecto.

7. Cómo probar RecyclerView

  1. Una vez que el proyecto se sincronizó, regresa al archivo AffirmationsListTests.kt. Proporciona un ViewInteraction para realizar una acción con onView(). El método onView() requiere que se pase un ViewMatcher. Usa withId() y asegúrate de pasar el ID de la RecyclerView que se usó para las afirmaciones. Ahora llama a perform() en ViewInteraction. Aquí es donde entra en juego la dependencia recién agregada. Ahora se puede pasar RecyclerViewActions.scrollToPosition<RecyclerView.Viewholder>(9) ViewAction.
onView(withId(R.id.recycler_view)).perform(
   RecyclerViewActions
       .scrollToPosition<RecyclerView.ViewHolder>(9)
)

Entender la sintaxis de esta línea no es fundamental, pero vale la pena explorarla. El nombre RecyclerViewActions es exactamente lo que implica: una clase que permite que tus pruebas realicen acciones en RecyclerView. scrollToPosition() es un método estático de la clase RecyclerViewActions que se desplazará hasta una posición especificada. Este método muestra lo que se conoce como genérico. Los elementos genéricos están fuera del alcance de este codelab, pero, en este caso, puedes considerarlos como el método scrollToPosition() que muestra cualquier elemento en RecyclerView, que podría ser cualquier cosa.

En nuestra app, los elementos de nuestra RecyclerView son ViewHolder, por lo que colocamos un par de corchetes angulares después de la llamada de método y en ellos especificamos RecyclerView.ViewHolder. Por último, pasa la posición final en la lista (9).

  1. Ahora que el desplazamiento a la posición deseada de RecyclerView está habilitado, realiza una aserción para asegurarte de que la IU muestre la información esperada. Asegúrate de que una vez que te desplaces hasta el último elemento, se mostrará el texto asociado con la afirmación final. Comienza con un ViewInteraction, pero esta vez pasa un nuevo ViewMatcher (en este caso, withText()). Para ese método, pasa el recurso de cadenas que contiene el texto de la última afirmación. El método withText() identifica un componente de IU en función del texto que muestra. Para este componente, lo único que debes hacer es comprobar que muestre el texto deseado. Para ello, se debe llamar a check() en ViewInteraction. check() requiere ViewAssertion, para lo que puedes usar el método matches(). Por último, pasa el método isDisplayed() para realizar la aserción de que se muestra el componente de la IU.
onView(withText(R.string.affirmation10))
    .check(matches(isDisplayed()))

Si volvemos a la nota sobre la codificación de posición a la que nos desplazaremos, hay una forma en la que se esto se puede anular usando RecyclerViewActions. Cuando no conozcas la longitud de la lista, podrás usar la acción scrollTo. La función scrollTo requiere Matcher<View!>! para encontrar un elemento específico. Esto puede ser diferentes elementos, pero para los fines de esta prueba, usa withText. Si lo aplicas a la prueba que acabas de escribir, el código debería verse de esta manera:

onView(withId(R.id.recycler_view)).perform(
   RecyclerViewActions
       .scrollTo<RecyclerView.ViewHolder>(
           withText(R.string.affirmation10)
       )
)

onView(withText(R.string.affirmation10))
    .check(matches(isDisplayed())
)

Ya está todo listo para ejecutar la prueba. Deberías ver que el dispositivo o el emulador se desplazan hasta la parte inferior de la lista y, luego, que la prueba es satisfactoria. Para asegurarte de que el resultado de la prueba sea correcto, reemplaza el ID de la cadena por R.string.affirmation1. Después de desplazarse, este recurso de cadenas no se muestra y la prueba debe fallar.

Hay varios métodos en la clase RecyclerViewActions, y te recomendamos que consultes los métodos disponibles.

8. Cómo crear una clase de prueba local

Crea una clase nueva en test -> com.example.affirmations llamada AffirmationsAdapterTests.kt.

9. Cómo agregar dependencias de prueba locales

  1. Anteriormente en este codelab, analizamos tres tipos diferentes de implementaciones de dependencias y agregaste una dependencia para las pruebas de instrumentación. Ahora, agrega una dependencia para las pruebas locales. Navega a app -> build.gradle y agrega lo siguiente como dependencia de prueba de unidades:
org.mockito:mockito-core:3.12.4

Las dependencias deberían ser parecidas a lo siguiente:

dependencies {
    ...
    testImplementation 'org.mockito:mockito-core:3.12.4'
}
  1. Ahora, sincroniza el proyecto.

10. Cómo probar el adaptador

Esta app en particular no se presta a la prueba de unidades, ya que no hay mucha lógica para probar. Sin embargo, podemos obtener más experiencia para probar varios componentes como preparación para pruebas futuras.

  1. Coloca la siguiente línea en la clase de prueba de unidades:
private val context = mock(Context::class.java)

El método mock() proviene de la biblioteca que acabamos de implementar en nuestro proyecto. La simulación es una parte integral de las pruebas de unidades, pero está fuera del alcance de este codelab. En otro codelab, analizaremos las simulaciones con más detalle. En Android, Context es el contexto del estado actual de la app, pero recuerda que las pruebas de unidades se ejecutan en la JVM y no en un dispositivo real, por lo que no hay Context. El método simulado nos permite crear una instancia "simulada" de Context. No tiene ninguna funcionalidad real, pero se puede usar para probar métodos que requieran un contexto.

  1. Crea una función llamada adapter_size() y anótala como prueba. El objetivo de esta prueba es asegurarse de que el tamaño del adaptador sea el tamaño de la lista que se pasó al adaptador. Para ello, crea una instancia de ItemAdapter y pasa la lista que muestra el método loadAffirmations() en la clase Datasource. De forma alternativa, crea una lista nueva y pruébala. Para las pruebas de unidades, se recomienda crear datos propios únicos de prueba, por lo que crearemos una lista personalizada para esta prueba.
val data = listOf(
   Affirmation(R.string.affirmation1, R.drawable.image1),
   Affirmation(R.string.affirmation2, R.drawable.image2)
)
  1. Ahora, crea una instancia de ItemAdapter y pasa las variables context y data creadas en los pasos anteriores.
val adapter = ItemAdapter(context, data)

Los adaptadores de vista de reciclador tienen un método que muestra el tamaño del adaptador llamado getItemCount(). En esta app, el método se ve de la siguiente manera:

/**
* Return the size of your dataset (invoked by the layout manager)
*/
override fun getItemCount() = dataset.size
  1. Este es el método que se debe probar. Asegúrate de que el valor que se muestre de este método coincida con el tamaño de la lista que creaste en el paso 2. Usa el método assertEquals() y compara los valores del tamaño de la lista y el del adaptador.
assertEquals("ItemAdapter is not the correct size", data.size, adapter.itemCount)

Ya estás familiarizado con el método assertEquals(), pero vale la pena examinar la línea para ser exhaustivo. El primer parámetro es una cadena que se muestra en el resultado de la prueba si esta falla. El segundo parámetro es el valor esperado. El tercer parámetro es el valor real. Tu clase de prueba debería verse así:

f81a27f5c1cf055e.png

  1. Ahora, ejecuta la prueba.

11. Código de solución

12. Felicitaciones

En este codelab, lograste lo siguiente:

  • Aprendiste a agregar dependencias específicas de pruebas.
  • Aprendiste a interactuar con RecyclerView mediante pruebas de instrumentación.
  • Analizaste algunas de las prácticas recomendadas principales para realizar pruebas.