Aspectos básicos de las pruebas de apps para Android

En esta página, se describen los principios básicos de las pruebas de apps para Android, incluidas las prácticas recomendadas centrales y sus beneficios.

Beneficios de las pruebas

Probar la app es una parte integral del proceso de desarrollo. Cuando ejecutas pruebas de manera constante, puedes verificar la precisión, el comportamiento funcional y la usabilidad de la app antes de lanzarla públicamente.

Puedes probar tu app manualmente navegando por ella. Puedes usar diferentes dispositivos y emuladores, cambiar el idioma del sistema y tratar de generar todos los errores del usuario o recorrer todos los flujos de usuarios.

Sin embargo, las pruebas manuales no se escalan bien y puede ser fácil pasar por alto las regresiones en el comportamiento de la app. Las pruebas automatizadas implican el uso de herramientas que realizan pruebas por ti, lo que es más rápido, más repetible y, por lo general, te brinda comentarios más prácticos sobre tu app en una etapa más temprana del proceso de desarrollo.

Tipos de pruebas en Android

Las aplicaciones para dispositivos móviles son complejas y deben funcionar bien en muchos entornos. Por lo tanto, existen muchos tipos de pruebas.

Asunto

Por ejemplo, existen diferentes tipos de pruebas según el asunto:

  • Pruebas funcionales: ¿Mi app hace lo que se supone que debe hacer?
  • Pruebas de rendimiento: ¿Lo hace de forma rápida y eficiente?
  • Pruebas de accesibilidad: ¿Funciona bien con los servicios de accesibilidad?
  • Pruebas de compatibilidad: ¿Funciona bien en todos los dispositivos y niveles de API?

Alcance

Las pruebas también varían según el tamaño o el grado de aislamiento:

  • Las pruebas de unidades o pruebas de nivel inferior solo verifican una porción muy pequeña de la app, como un método o una clase.
  • Las pruebas de extremo a extremo o pruebas de nivel superior verifican partes más grandes de la app al mismo tiempo, como una pantalla completa o un flujo de usuarios.
  • Las pruebas de nivel medio se encuentran en el medio y verifican la integración entre dos o más unidades.
Las pruebas pueden ser pequeñas, medianas o grandes.
Figura 1: Alcances de prueba en una aplicación típica.

Existen muchas formas de clasificar las pruebas. Sin embargo, la distinción más importante para los desarrolladores de apps es dónde se ejecutan las pruebas.

Pruebas instrumentadas versus pruebas locales

Puedes ejecutar pruebas en un dispositivo Android o en otra computadora:

  • Las pruebas instrumentadas se ejecutan en un dispositivo Android, ya sea físico o emulado. La app se compila y se instala junto con una app de prueba que inserta comandos y lee el estado. Las pruebas instrumentadas suelen ser pruebas de IU, que inician una app y luego interactúan con ella.
  • Las pruebas locales se ejecutan en tu máquina de desarrollo o en un servidor, por lo que también se denominan pruebas del lado del host. Por lo general, son pequeñas y rápidas, y aíslan el asunto en prueba del resto de la app.
Las pruebas pueden ejecutarse como pruebas instrumentadas en un dispositivo o como pruebas locales en tu máquina de desarrollo.
Figura 2: Diferentes tipos de pruebas según dónde se ejecutan.

No todas las pruebas de unidades son locales, y no todas las pruebas de extremo a extremo se ejecutan en un dispositivo. Por ejemplo:

  • Prueba local de nivel superior: Puedes usar un simulador de Android que se ejecute de forma local, como Robolectric.
  • Prueba instrumentada de nivel inferior: Puedes verificar que tu código funcione bien con una función del framework, como una base de datos SQLite. Puedes ejecutar esta prueba en varios dispositivos para verificar la integración con varias versiones de SQLite.

Ejemplos

En los siguientes fragmentos, se muestra cómo interactuar con la IU en una prueba de IU instrumentada que hace clic en un elemento y verifica que se muestre otro elemento.

Espresso

// When the Continue button is clicked
onView(withText("Continue"))
    .perform(click())

// Then the Welcome screen is displayed
onView(withText("Welcome"))
    .check(matches(isDisplayed()))

IU de Compose

// When the Continue button is clicked
composeTestRule.onNodeWithText("Continue").performClick()

// Then the Welcome screen is displayed
composeTestRule.onNodeWithText("Welcome").assertIsDisplayed()

En este fragmento, se muestra parte de una prueba de unidades para un ViewModel (prueba local del lado del host):

// Given an instance of MyViewModel
val viewModel = MyViewModel(myFakeDataRepository)

// When data is loaded
viewModel.loadData()

// Then it should be exposing data
assertTrue(viewModel.data != null)

Arquitectura que se puede probar

Con una arquitectura de app que se puede probar, el código sigue una estructura que te permite probar fácilmente diferentes partes de forma aislada. Las arquitecturas que se pueden probar tienen otras ventajas, como una mejor legibilidad, capacidad de mantenimiento, escalabilidad y reutilización.

Una arquitectura que no se puede probar produce lo siguiente:

  • Pruebas más grandes, lentas y más inestables. Es posible que las clases que no se pueden probar con pruebas de unidades deban cubrirse con pruebas de integración o pruebas de IU más grandes.
  • Menos oportunidades para probar diferentes situaciones. Las pruebas más grandes son más lentas, por lo que probar todos los estados posibles de una app podría ser poco realista.

Para obtener más información sobre los lineamientos de arquitectura, consulta la guía de arquitectura de apps.

Enfoques para el desacoplamiento

Si puedes extraer parte de una función, clase o módulo del resto, la prueba es más fácil y efectiva. Esta práctica se conoce como desacoplamiento, y es el concepto más importante para la arquitectura que se puede probar.

Algunas técnicas comunes de desacoplamiento son las siguientes:

  • Divide una app en capas como Presentación, Dominio y Datos. También puedes dividir una app en módulos, uno por función.
  • Evita agregar lógica a entidades que tengan dependencias grandes, como actividades y fragmentos. Usa estas clases como puntos de entrada al framework y mueve la IU y la lógica empresarial a otro lugar, como a un elemento componible, ViewModel o capa de dominio.
  • Evita las dependencias directas del framework en las clases que contienen lógica empresarial. Por ejemplo, no uses contextos de Android en ViewModels.
  • Facilita el reemplazo de dependencias. Por ejemplo, usa interfaces en lugar de implementaciones concretas. Usa la inyección de dependencias, incluso si no usas un framework de DI.

Próximos pasos

Ahora que sabes por qué debes realizar pruebas y los dos tipos principales de pruebas, puedes leer Qué probar o obtener información sobre estrategias de prueba

Como alternativa, si quieres crear tu primera prueba y aprender mientras lo haces, consulta los codelabs de pruebas.