Las pruebas automatizadas te ayudan a mejorar la calidad de la app de varias maneras. Por ejemplo, ayuda a realizar la validación, detectar regresiones y verificar la compatibilidad. Una buena estrategia de pruebas te permite aprovechar las pruebas automatizadas para enfocarte en un beneficio importante: la productividad del desarrollador.
Los equipos alcanzan niveles más altos de productividad cuando usan un enfoque sistemático para las pruebas junto con mejoras en la infraestructura. De esta manera, se proporcionan comentarios oportunos sobre el comportamiento del código. Una buena estrategia de prueba hace lo siguiente:
- Detecta los problemas lo antes posible.
- Se ejecuta con rapidez.
- Proporciona indicaciones claras cuando se debe corregir algo.
Esta página te ayudará a decidir qué tipos de pruebas implementar, dónde ejecutarlas y con qué frecuencia.
La pirámide de pruebas
Puedes categorizar las pruebas en aplicaciones modernas por tamaño. Las pruebas pequeñas se enfocan solo en una pequeña parte del código, lo que las hace rápidas y confiables. Las pruebas grandes tienen un alcance amplio y requieren configuraciones más complejas que son difíciles de mantener. Sin embargo, las pruebas grandes tienen más fidelidad* y pueden descubrir muchos más problemas de una sola vez.
*Fidelidad se refiere a la similitud del entorno de ejecución de prueba con el entorno de producción.
La mayoría de las apps deben tener muchas pruebas pequeñas y relativamente pocas pruebas grandes. La distribución de las pruebas en cada categoría debe formar una pirámide, con más pruebas pequeñas que forman la base y las menos numerosas grandes que forman la punta.
Minimiza el costo de un error
Una buena estrategia de pruebas maximiza la productividad de los desarrolladores y, al mismo tiempo, minimiza el costo de encontrar errores.
Considera un ejemplo de una estrategia posiblemente ineficaz. Aquí, la cantidad de pruebas por tamaño no se organiza en una pirámide. Hay demasiadas pruebas de extremo a extremo grandes y muy pocas pruebas de IU de componentes:
Esto significa que se ejecutan muy pocas pruebas antes de la combinación. Si hay un error, es posible que las pruebas no lo detecten hasta que se ejecuten las pruebas de extremo a extremo nocturnas o semanales.
Es importante considerar las implicaciones que esto tiene para el costo de identificar y corregir errores, y por qué es importante sesgar tus esfuerzos de prueba hacia pruebas más pequeñas y frecuentes:
- Cuando una prueba de unidades detecta el error, por lo general, se corrige en minutos, por lo que el costo es bajo.
- Una prueba de extremo a extremo podría tardar días en descubrir el mismo error. Esto podría atraer a varios miembros del equipo, lo que reduciría la productividad general y, posiblemente, retrasaría un lanzamiento. El costo de este error es más alto.
Dicho esto, una estrategia de pruebas ineficiente es mejor que no tener ninguna. Cuando un error llega a producción, la corrección tarda mucho en llegar a los dispositivos del usuario, a veces semanas, por lo que el ciclo de reacción es el más largo y costoso.
Una estrategia de pruebas escalable
Tradicionalmente, la pirámide de pruebas se divide en 3 categorías:
- Pruebas de unidades
- Pruebas de integración
- Pruebas de extremo a extremo.
Sin embargo, estos conceptos no tienen definiciones precisas, por lo que los equipos pueden definir sus categorías de manera diferente, por ejemplo, con 5 capas:
- Una prueba de unidad se ejecuta en la máquina host y verifica una sola unidad funcional de lógica sin dependencias en el framework de Android.
- Ejemplo: verificar errores por un paso en una función matemática.
- Una prueba de componente verifica la funcionalidad o el aspecto de un módulo o componente de forma independiente de otros componentes del sistema. A diferencia de las pruebas de unidades, la superficie de una prueba de componentes se extiende a abstracciones más altas por sobre métodos y clases individuales.
- Ejemplo: Prueba de captura de pantalla para un botón personalizado
- Una prueba de función verifica la interacción de dos o más componentes o módulos independientes. Las pruebas de funciones son más grandes y complejas, y
suelen operar a nivel de la función.
- Ejemplo: Pruebas de comportamiento de la IU que verifican la administración de estado en una pantalla
- Una prueba de aplicación verifica la funcionalidad de toda la aplicación en forma de un objeto binario implementable. Son pruebas de integración grandes que usan un objeto binario depurable, como una compilación de desarrollo que puede contener hooks de prueba, como el sistema a prueba.
- Ejemplo: Prueba de comportamiento de la IU para verificar los cambios de configuración en pruebas de accesibilidad, localización y plegables
- Una prueba de versión candidata verifica la funcionalidad de una compilación de lanzamiento.
Son similares a las pruebas de aplicaciones, excepto que el objeto binario de la aplicación está reducido y optimizado. Estas son pruebas de integración de extremo a extremo de gran tamaño que se ejecutan en un entorno lo más cercano posible a la producción sin exponer la app a cuentas de usuario públicas o backends públicos.
- Ejemplo: Recorridos críticos del usuario, pruebas de rendimiento
Esta categorización tiene en cuenta la fidelidad, el tiempo, el alcance y el nivel de aislamiento. Puedes tener diferentes tipos de pruebas en varias capas. Por ejemplo, la capa de prueba de la aplicación puede contener pruebas de comportamiento, capturas de pantalla y rendimiento.
Alcance |
Acceso a la red |
Ejecución |
Tipo de compilación |
Ciclo de vida |
|
---|---|---|---|---|---|
Unidad |
Método o clase únicos con dependencias mínimas |
No |
Local |
Depurable |
Antes de la combinación |
Componente |
Nivel del módulo o componente Varias clases juntas |
No |
Local |
Depurable |
Antes de la combinación |
Función |
Nivel de componente Integración con componentes que pertenecen a otros equipos |
Simulación |
Dispositivos |
Depurable |
Fusión previa |
Aplicación |
Nivel de aplicación Integración con funciones o servicios que son propiedad de otros equipos |
Simulado |
Emulador |
Depurable |
Antes de la combinación |
Versión candidata |
Nivel de aplicación Integración con funciones o servicios que pertenecen a otros equipos |
Servidor de producción |
Emulador |
Compilación de versión reducida |
Después de la combinación |
Decide la categoría de prueba
Como regla general, debes considerar la capa más baja de la pirámide que pueda brindar al equipo el nivel correcto de retroalimentación.
Por ejemplo, considera cómo probar la implementación de esta función: la IU de un flujo de acceso. Según lo que necesites probar, elegirás diferentes categorías:
Sujeto a prueba |
Descripción de lo que se está probando |
Categoría de pruebas |
Ejemplo de tipo de prueba |
---|---|---|---|
Lógica del validador de formularios |
Es una clase que valida la dirección de correo electrónico con una expresión regular y verifica que se haya ingresado el campo de contraseña. No tiene dependencias. |
Pruebas de unidades |
|
Comportamiento de la IU del formulario de acceso |
Un formulario con un botón que solo se habilita cuando se valida el formulario |
Pruebas de componentes |
Prueba de comportamiento de la IU que se ejecuta en Robolectric |
Aspecto de la IU del formulario de acceso |
Un formulario que sigue una especificación de UX |
Pruebas de componentes |
|
Integración con el administrador de autenticación |
La IU que envía credenciales a un administrador de autenticación y recibe respuestas que pueden contener diferentes errores. |
Pruebas de funciones |
|
Diálogo de acceso |
Una pantalla que muestra el formulario de acceso cuando se presiona el botón de acceso. |
Pruebas de aplicaciones |
Prueba de comportamiento de la IU que se ejecuta en Robolectric |
Recorrido crítico del usuario: Acceso |
Un flujo de acceso completo con una cuenta de prueba en un servidor de pruebas |
Versión potencial |
Prueba de comportamiento de la IU de Compose de extremo a extremo que se ejecuta en el dispositivo |
En algunos casos, que algo pertenezca a una categoría o a otra puede ser subjetivo. Puede haber otros motivos por los que una prueba se mueve hacia arriba o hacia abajo, como el costo de la infraestructura, la inestabilidad y los tiempos de prueba largos.
Ten en cuenta que la categoría de prueba no determina el tipo de prueba y que no todas las funciones se deben probar en todas las categorías.
Las pruebas manuales también pueden ser parte de tu estrategia de pruebas. Por lo general, los equipos de QA realizan pruebas de lanzamiento candidatas, pero también pueden participar en otras etapas. Por ejemplo, pruebas exploratorias de errores en una función sin una secuencia de comandos.
Infraestructura de pruebas
Una estrategia de pruebas debe estar respaldada por infraestructura y herramientas para ayudar a los desarrolladores a ejecutar continuamente sus pruebas y aplicar reglas que garanticen que todas las pruebas se aprueban.
Puedes categorizar las pruebas por alcance para definir cuándo y dónde ejecutarlas. Por ejemplo, siguiendo el modelo de 5 capas:
Categoría |
Entorno (dónde) |
Activador (cuándo) |
---|---|---|
Unidad |
[Local][4] |
Cada confirmación |
Componente |
Local |
Cada confirmación |
Función |
Local y emuladores |
Antes de la combinación, antes de combinar o enviar un cambio |
Aplicación |
Local, emuladores, 1 teléfono y 1 plegable |
Después de la combinación, después de combinar o enviar un cambio |
Versión candidata |
8 teléfonos diferentes, 1 dispositivo plegable y 1 tablet |
Antes del lanzamiento |
- Las pruebas de unidad y componente se ejecutan en el sistema de integración continua para cada confirmación nueva, pero solo para los módulos afectados.
- Todas las pruebas de unidad, componente y función se ejecutan antes de combinar o enviar un cambio.
- Las pruebas de aplicación se ejecutan después de la combinación.
- Las pruebas de versiones candidatas se ejecutan todas las noches en un teléfono, un dispositivo plegable y una tablet.
- Antes de una versión, las pruebas de Versión candidata se ejecutan en una gran cantidad de dispositivos.
Estas reglas pueden cambiar con el tiempo cuando la cantidad de pruebas afecta la productividad. Por ejemplo, si pasas las pruebas a una cadencia nocturna, puedes disminuir los tiempos de compilación y prueba de CI, pero también puedes prolongar el ciclo de comentarios.