Prueba de las interfaces de usuario de Android con espresso

En esta publicación, aprenderá cómo escribir pruebas de UI con el marco de prueba de Espresso y automatizar su flujo de trabajo de prueba, en lugar de usar el proceso manual tedioso y altamente propenso a errores. 

Espresso es un marco de prueba para escribir pruebas de interfaz de usuario en Android. Según los documentos oficiales, usted puede:

Use Espresso para escribir pruebas de IU de Android concisas, hermosas y confiables.

1. ¿Por qué usar el espresso??

Uno de los problemas con las pruebas manuales es que puede llevar mucho tiempo y ser tedioso de realizar. Por ejemplo, para probar una pantalla de inicio de sesión (manualmente) en una aplicación de Android, deberá hacer lo siguiente:

  1. Lanzar la aplicación. 
  2. Navegue a la pantalla de inicio de sesión. 
  3. Confirmar si el usernameEditText y passwordEditText son visibles. 
  4. Escriba el nombre de usuario y la contraseña en sus respectivos campos. 
  5. Confirme si el botón de inicio de sesión también está visible, y luego haga clic en ese botón de inicio de sesión.
  6. Compruebe si se muestran las vistas correctas cuando el inicio de sesión fue exitoso o fue un error. 

¡En lugar de pasar todo este tiempo probando manualmente nuestra aplicación, sería mejor dedicar más tiempo a escribir un código que haga que nuestra aplicación se destaque del resto! Y, aunque las pruebas manuales son tediosas y bastante lentas, siguen siendo propensas a errores y es posible que se pierda algunos casos de esquina.. 

Algunas de las ventajas de las pruebas automatizadas incluyen las siguientes:   

  • Las pruebas automatizadas ejecutan exactamente los mismos casos de prueba cada vez que se ejecutan. 
  • Los desarrolladores pueden detectar rápidamente un problema antes de enviarlo al equipo de control de calidad. 
  • Puede ahorrar mucho tiempo, a diferencia de hacer pruebas manuales. Al ahorrar tiempo, los ingenieros de software y el equipo de control de calidad pueden dedicar más tiempo a tareas desafiantes y gratificantes. 
  • Se logra una mayor cobertura de prueba, lo que conduce a una aplicación de mejor calidad. 

En este tutorial, aprenderemos sobre Espresso integrándolo en un proyecto de Android Studio. Escribiremos pruebas de UI para una pantalla de inicio de sesión y una RecyclerView, y aprenderemos sobre los intentos de prueba. 

La calidad no es un acto, es un hábito. - Pablo Picasso

2. Requisitos previos

Para poder seguir este tutorial, necesitarás:

  • una comprensión básica de las API básicas de Android y Kotlin
  • Android Studio 3.1.3 o superior
  • Kotlin plugin 1.2.51 o superior

Puede encontrar un proyecto de muestra (en Kotlin) para este tutorial en nuestro repositorio de GitHub para que pueda seguirlo fácilmente..

3. Crea un proyecto de Android Studio

Encienda su Android Studio 3 y cree un nuevo proyecto con una actividad vacía llamada Actividad principal. Asegúrese de comprobar Incluye soporte de Kotlin.

4. Configurar Espresso y AndroidJUnitRunner

Después de crear un nuevo proyecto, asegúrese de agregar las siguientes dependencias de la biblioteca de soporte de pruebas de Android en su construir.gradle (aunque Android Studio ya los ha incluido para nosotros). En este tutorial, estamos utilizando la última versión de la biblioteca Espresso 3.0.2 (en el momento de escribir este artículo). 

android // ... defaultConfig // ... testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" // ... dependencies // ... androidTestImplementation 'com.android.support.test.espresso: espresso-core: 3.0. 2 'androidTestImplementation' com.android.support.test: runner: 1.0.2 'androidTestImplementation' com.android.support.test: rules: 1.0.2 '

También incluimos el corredor de instrumentación. AndroidJUnitRunner:

Un Instrumentación que ejecuta las pruebas JUnit3 y JUnit4 contra un paquete de Android (aplicación).

Tenga en cuenta que Instrumentación es simplemente una clase base para implementar código de instrumentación de aplicación. 

Desactivar la animación 

La sincronización de Espresso, que no sabe cómo esperar a que termine una animación, puede hacer que algunas pruebas fallen, si permite la animación en su dispositivo de prueba. Para desactivar la animación en su dispositivo de prueba, vaya a Ajustes > Opciones de desarrollador y desactive todas las siguientes opciones en la sección "Dibujo": 

  • Escala de animación de ventana
  • Escala de animación de transición
  • Escala de duración del animador

5. Escribe tu primera prueba en espresso

Primero, comenzamos a probar una pantalla de inicio de sesión. Así es como comienza el flujo de inicio de sesión: el usuario inicia la aplicación y la primera pantalla que se muestra contiene una sola Iniciar sesión botón. Cuando eso Iniciar sesión Se hace clic en el botón, se abre el Iniciar sesiónActividad pantalla. Esta pantalla contiene solo dos Editar textos (los campos de nombre de usuario y contraseña) y un Enviar botón. 

Esto es lo que nuestro Actividad principal el diseño se ve como

Esto es lo que nuestro Iniciar sesiónActividad el diseño se ve como

Ahora escribamos una prueba para nuestro Actividad principal clase. Ir a tu Actividad principal clase, mueva el cursor a la Actividad principal nombre y presione Shift-Control-T. Seleccionar Crear nueva prueba ... en el menú emergente. 

presione el DE ACUERDO botón, y aparece otro diálogo. Elegir la prueba de android directorio y haga clic en el DE ACUERDO botón una vez más. Tenga en cuenta que debido a que estamos escribiendo una prueba de instrumentación (pruebas específicas de Android SDK), los casos de prueba residen en el prueba de android / java carpeta. 

Ahora, Android Studio ha creado con éxito una clase de prueba para nosotros. Por encima del nombre de la clase, incluya esta anotación: @RunWith (AndroidJUnit4 :: class).

importar android.support.test.runner.AndroidJUnit4 importar org.junit.runner.RunWith @RunWith (AndroidJUnit4 :: class) class MainActivityTest 

Esta anotación significa que todas las pruebas en esta clase son pruebas específicas de Android.

Actividades de prueba

Como queremos probar una actividad, tenemos que hacer una pequeña configuración. Necesitamos informar a Espresso qué actividad abrir o iniciar antes de ejecutar y destruir después de ejecutar cualquier método de prueba. 

import android.support.test.rule.ActivityTestRule import android.support.test.runner.AndroidJUnit4 import org.junit.Rule import org.junit.runner.RunWith @RunWith (AndroidJUnit4 :: class) class MainActivityTest @Rule @vmcc activityRule = ActivityTestRule(MainActivity :: class.java)

Tenga en cuenta que el @Regla anotación significa que esta es una regla de prueba JUnit4. Las reglas de prueba de JUnit4 se ejecutan antes y después de cada método de prueba (anotadas con @Prueba). En nuestro propio escenario, queremos lanzar Actividad principal antes de cada método de prueba y destruirlo después. 

También incluimos el @JvmField Anotación de Kotlin. Esto simplemente indica al compilador que no genere captadores y definidores para la propiedad y, en cambio, lo exponga como un simple campo Java..

Aquí están los tres pasos principales para escribir una prueba de Espresso:

  • Busque el widget (por ejemplo,. Vista de texto o Botón) quieres probar.
  • Realiza una o más acciones en ese widget. 
  • Verifique o verifique si ese widget está ahora en un estado determinado. 

Los siguientes tipos de anotaciones se pueden aplicar a los métodos utilizados dentro de la clase de prueba.

  • @Antes de clase: esto indica que el método estático al que se aplica esta anotación debe ejecutarse una vez y antes de todas las pruebas en la clase. Esto podría usarse, por ejemplo, para configurar una conexión a una base de datos. 
  • @Antes de: indica que el método al que se adjunta esta anotación debe ejecutarse antes de cada método de prueba en la clase.
  • @Prueba: indica que el método al que se adjunta esta anotación debe ejecutarse como un caso de prueba.
  • @Después: indica que el método al que se adjunta esta anotación debe ejecutarse después de cada método de prueba. 
  • @Después de clases: indica que el método al que se adjunta esta anotación debe ejecutarse después de que se hayan ejecutado todos los métodos de prueba en la clase. Aquí, normalmente cerramos los recursos que se abrieron en @Antes de clase

Encontrar un Ver Utilizando a la vista()

En nuestro Actividad principal archivo de diseño, solo tenemos un widget-el Iniciar sesión botón. Probemos un escenario donde un usuario encuentre ese botón y haga clic en él.

importar android.support.test.espresso.Espresso.onView importar android.support.test.espresso.matcher.ViewMatchers.withId //… @RunWith (AndroidJUnit4 :: class) clase MainActivityTest //… @Test @Throws (Excepción: : class) fun clickLoginButton_opensLoginUi () onView (withId (R.id.btn_login))

Para encontrar widgets en Espresso, hacemos uso del a la vista() método estático (en lugar de findViewById ()). El tipo de parámetro que suministramos a a la vista() es un Matcher. Tenga en cuenta que el Matcher API no proviene del SDK de Android, sino del Proyecto Hamcrest. La biblioteca de combinaciones de Hamcrest está dentro de la biblioteca de espresso que sacamos a través de Gradle. 

los onView (withId (R.id.btn_login)) devolverá un ViewInteraction eso es para un Ver cuya ID es R.id.btn_login. En el ejemplo anterior, usamos withId () para buscar un widget con un id dado. Otros comparadores de vistas que podemos usar son: 

  • conTexto (): devuelve un matcher que coincide Vista de texto basado en su valor de propiedad de texto.
  • withHint (): devuelve un matcher que coincide Vista de texto basado en su valor de propiedad de pista.
  • conTagKey (): devuelve un matcher que coincide Ver basado en claves de etiquetas.
  • withTagValue (): devuelve un matcher que coincide Vers basado en valores de propiedad de etiqueta.

Primero, probemos para ver si el botón se muestra realmente en la pantalla. 

onView (withId (R.id.btn_login)). check (coincide (isDisplayed ()))

Aquí, solo estamos confirmando si el botón con la ID dada (R.id.btn_login) es visible para el usuario, por lo que usamos el comprobar() método para confirmar si el subyacente Ver tiene un cierto estado, en nuestro caso, si es visible.

los partidos() Método estático devuelve un genérico ViewAssertion que afirma que existe una vista en la jerarquía de vistas y que coincide con el coincidente de vista dado. Ese visor de coincidencias dado se devuelve llamando se visualiza(). Según lo sugerido por el nombre del método, se visualiza() es un matcher que coincide Vers que se muestran actualmente en la pantalla para el usuario. Por ejemplo, si queremos comprobar si un botón está habilitado, simplemente pasamos está habilitado() a partidos()

Otros concordantes de vistas populares que podemos pasar a la partidos() método son:

  • hasFocus (): devuelve un matcher que coincide Vers que actualmente tienen foco.
  • está chequeado(): devuelve un comparador que acepta si y solo si la vista es un CompoundButton (o subtipo de) y está en estado marcado. Lo contrario de este método es isNotChecked ()
  • isSelected (): devuelve un matcher que coincide Vers que son seleccionados.

Para ejecutar la prueba, puede hacer clic en el triángulo verde junto al método o al nombre de la clase. Al hacer clic en el triángulo verde al lado del nombre de la clase, se ejecutarán todos los métodos de prueba en esa clase, mientras que el que está al lado del método ejecutará la prueba solo para ese método. 

¡Hurra! Nuestra prueba pasó!


Realizar acciones en una vista

En un ViewInteraction objeto que se devuelve llamando a la vista(), Podemos simular acciones que un usuario puede realizar en un widget. Por ejemplo, podemos simular una acción de clic simplemente llamando al hacer clic() método estático dentro de la VerAcciones clase. Esto devolverá un ViewAction objeto para nosotros. 

La documentación dice que ViewAction es:

Responsable de realizar una interacción en el elemento Ver dado.
@Test fun clickLoginButton_opensLoginUi () //… onView (withId (R.id.btn_login)). Perform (click ())

Realizamos un evento clic por primera llamada. realizar(). Este método realiza la (s) acción (es) dada (s) en la vista seleccionada por el comparador de vista actual. Tenga en cuenta que podemos pasarle una sola acción o una lista de acciones (ejecutadas en orden). Aquí lo dimos. hacer clic(). Otras posibles acciones son:

  • teclee el texto() para imitar escribiendo texto en una Editar texto.
  • Borrar texto() para simular la limpieza de texto en una Editar texto.
  • haga doble clic() para simular hacer doble clic en un Ver.
  • longClick () para imitar a un clic largo Ver.
  • scrollTo () para simular el desplazamiento de un ScrollView a un particular Ver eso es visible. 
  • deslizar a la izquierda() para simular deslizar de derecha a izquierda a través del centro vertical de una Ver.

Muchas simulaciones más se pueden encontrar dentro de la VerAcciones clase. 

Validar con ver aserciones

Vamos a completar nuestra prueba, para validar que el Iniciar sesiónActividad la pantalla se muestra cada vez que el Iniciar sesión Se hace clic en el botón. Aunque ya hemos visto como usar comprobar() en un ViewInteraction, Usémoslo de nuevo, pasándolo por otro. ViewAssertion

@Test fun clickLoginButton_opensLoginUi () //… onView (withId (R.id.tv_login)). Check (coincide (isDisplayed ()))

Dentro de Iniciar sesiónActividad archivo de diseño, aparte de Editar textos y a Botón, también tenemos un Vista de texto con identificación R.id.tv_login. Así que simplemente hacemos un cheque para confirmar que el Vista de texto es visible para el usuario. 

Ahora puedes volver a ejecutar la prueba.!

Tus pruebas deberían pasar con éxito si seguiste todos los pasos correctamente. 

Esto es lo que sucedió durante el proceso de ejecución de nuestras pruebas: 

  1. Lanzó el Actividad principal utilizando la actividadRegulación campo.
  2. Verificado si el Iniciar sesión botón (R.id.btn_login) era visible (se visualiza()) al usuario.
  3. Simuló una acción de clic (hacer clic()) en ese botón.
  4. Verificado si el Iniciar sesiónActividad se le mostró al usuario al verificar si una Vista de texto con identificación R.id.tv_login en el Iniciar sesiónActividad es visible.

Siempre puede consultar la hoja de trucos de Espresso para ver los diferentes igualadores de vista, ver acciones y ver aserciones disponibles. 

6. Probar el Iniciar sesiónActividad Pantalla

Aquí está nuestro LoginActivity.kt:

import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.widget.Button import android.widget.EditText import android.widget.TextView class LoginActivity: AppCompatActivity () private lateinit var usernameEditText: Edit loginTitleTextView: TextView private lateinit var passwordEditText: EditText recreat recreate .parte. = findViewById (R.id.et_password) submitButton = findViewById (R.id.btn_submit) loginTitleTextView = findViewById (R.id.tv_login) submitButton.permas en el / las (los) nombre de usuario (persona / nombre / usuario / siguiente). text.toString () == "password") loginTitleTextView.text = "Success" else else loginTitleTextView.text = "Failure"

En el código anterior, si el nombre de usuario ingresado es "chike" y la contraseña es "contraseña", el inicio de sesión es exitoso. Para cualquier otra entrada, es un fracaso. Ahora vamos a escribir una prueba de espresso para esto!

Ir LoginActivity.kt, mover el cursor a la Iniciar sesiónActividad nombre y presione Shift-Control-T. Seleccionar Crear nueva prueba ...  en el menú emergente. Siga el mismo proceso que hicimos para MainActivity.kt, y haga clic en el DE ACUERDO botón. 

import android.support.test.espresso.Espresso import android.support.test.espresso.Espresso.onView import android.support.test.espresso.action.ViewActions import android.support.test.espresso.assertion.ViewAssertions.matches import android .support.test.espresso.matcher.ViewMatchers.withId import android.support.test.espresso.matcher.ViewMatchers.withText import android.support.test.rule.ActivityTestRule import android.support.test.runner.AndroidJUnit4 import org.junit .Rule import org.junit.Test import org.junit.runner.RunWith @RunWith (AndroidJUnit4 :: class) clase LoginActivityTest @Rule @JvmField var activityRule = ActivityTestRule(LoginActivity :: class.java) private val username = "chike" private val password = "password" @Test fun ClickLoginButton_opensLoginUi () onView (withId (R.id.et_username)). Perform (ViewActions.typeText (nombre de usuario)) onView (withId (R.id.et_password)). perform (ViewActions.typeText (password)) onView (withId (R.id.btn_submit)). perform (ViewActions.scrollTo (), ViewActions.click ()) Espresso.onView (withId (R.id.tv_login)) .check (coincide con (withText ("Success")))

Esta clase de prueba es muy similar a la primera. Si corremos la prueba, nuestra Iniciar sesiónActividad se abre la pantalla. El nombre de usuario y la contraseña se escriben en el R.id.et_username y R.id.et_password campos respectivamente. A continuación, Espresso hará clic en el Enviar botón (R.id.btn_submit). Esperará hasta que un Ver con identificación R.id.tv_login se puede encontrar con la lectura de texto Éxito

7. Probar un RecyclerView

RecyclerViewActions es la clase que expone un conjunto de API para operar en una RecyclerView. RecyclerViewActions es parte de un artefacto separado dentro del expreso-contrib artefacto, que también debe ser añadido a construir.gradle:

androidTestImplementation 'com.android.support.test.espresso: espresso-contrib: 3.0.2' 

Tenga en cuenta que este artefacto también contiene la API para la interfaz de usuario que prueba el cajón de navegación a través de Cajón de acciones y Cajoneros

@RunWith (AndroidJUnit4 :: class) class MyListActivityTest //… @Test fun clickItem () onView (withId (R.id.rv)) .perform (RecyclerViewActions .actionOnItemAtPosition(0, ViewActions.click ()))

Para hacer clic en un elemento en cualquier posición en una RecyclerView, invocamos actionOnItemAtPosition (). Tenemos que darle un tipo de artículo. En nuestro caso, el artículo es el ViewHolder clase dentro de nuestro RandomAdapter. Este método también toma en dos parámetros; el primero es la posición, y el segundo es la acción (ViewActions.click ()). 

Otro RecyclerViewActions que se pueden realizar son:

  • actionOnHolderItem (): realiza un ViewAction en una vista emparejada por viewHolderMatcher. Esto nos permite igualarlo con lo que contiene el ViewHolder en lugar de la posición. 
  • scrollToPosition (): devuelve un ViewAction que se desplaza RecyclerView a una posición.

A continuación (una vez que la "pantalla de agregar nota" esté abierta), ingresaremos el texto de nuestra nota y guardaremos la nota. No necesitamos esperar a que se abra la nueva pantalla: Espresso lo hará automáticamente por nosotros. Espera hasta una vista con el id. R.id.add_note_title puede ser encontrado.

8. Intentos de prueba

El espresso hace uso de otro artefacto llamado intenciones de expreso para los intentos de prueba. Este artefacto es solo otra extensión de Espresso que se enfoca en la validación y burla de los Intentos. Veamos un ejemplo.

Primero, tenemos que tirar del intenciones de expreso biblioteca en nuestro proyecto. 

androidTestImplementation 'com.android.support.test.espresso: espresso-intents: 3.0.2'
import android.support.test.espresso.intent.rule.IntentsTestRule import android.support.test.runner.AndroidJUnit4 import org.junit.Rule import org.junit.runner.RunWith @RunWith (AndroidJUnit4 :: class) clase PickContactivoactividad Regla @JvmField var intentRule = IntentsTestRule(PickContactActivity :: class.java)

IntentsTestRule se extiende ActivityTestRule, por lo que ambos tienen comportamientos similares. Esto es lo que dice el doc:

Esta clase es una extensión de ActivityTestRule, que inicializa Espresso-Intents antes de cada prueba anotada con Prueba y libera Espresso-Intents después de cada prueba. La Actividad finalizará después de cada prueba y esta regla se puede utilizar de la misma manera que ActivityTestRule.

La principal característica diferenciadora es que tiene funcionalidades adicionales para la prueba. startActivity () y startActivityForResult () con burlas y talones. 

Ahora vamos a probar un escenario en el que un usuario hará clic en un botón (R.id.btn_select_contact) en la pantalla para elegir un contacto de la lista de contactos del teléfono. 

//… @Test fun stubPick () var result = Instrumentation.ActivityResult (Activity.RESULT_OK, Intent (null, ContactsContract.Contacts.CONTENT_URI)) intencional (hasAction (Intent.ACTION_PICK)) .responWith (resultado) onView (withId () R.id.btn_select_contact)). Realiza (click ()) intencionada (allOf (toPackage ("com.google.android.contacts"), hasAction (Intent.ACTION_PICK), hasData (ContactsContract.Contacts.CONTENT_URI))) // ...

Aquí estamos usando pretendiendo () desde el intenciones de expreso biblioteca para configurar un talón con una respuesta simulada para nuestra ACTION_PICK solicitud. Esto es lo que sucede en  PickContactActivity.kt cuando el usuario hace clic en el botón con id R.id.btn_select_contact para elegir un contacto.

diversión pickContact (v: Ver) val i = Intent (Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI) startActivityForResult (i, PICK_REQUEST)

pretendiendo () toma en un Matcher que coincida con los intentos para los que se debe proporcionar una respuesta aplastada. En otras palabras, la Matcher identifica qué solicitud estás interesado en aplastar. En nuestro propio caso, hacemos uso de hasAction () (un método de ayuda en IntentMatchers) para encontrar nuestro ACTION_PICK solicitud. Entonces invocamos responder con(), que establece el resultado para onActivityResult (). En nuestro caso, el resultado ha Actividad.RESULTADO_OK, simulando al usuario seleccionando un contacto de la lista. 

Luego simulamos haciendo clic en el botón seleccionar contacto, que llama startActivityForResult (). Tenga en cuenta que nuestro talón envió la respuesta simulada a onActivityResult ()

Finalmente, utilizamos el destinado a() método de ayuda para simplemente validar que las llamadas a startActivity () y startActivityForResult () fueron hechos con la información correcta. 

Conclusión

En este tutorial, aprendió a usar fácilmente el marco de prueba de Espresso en su proyecto de Android Studio para automatizar su flujo de trabajo de prueba. 

Recomiendo consultar la documentación oficial para obtener más información sobre cómo escribir pruebas de UI con Espresso.