Responsive testing & Cypress
Una solución escalable, dinámica y potente para el responsive testing con Cypress.
Cypress es una herramienta open source de automatización de tests para aplicaciones web. Comúnmente asociada a Selenium es, en cuanto a sus fundamentos y arquitectura, completamente distinta. Con una extensa y detallada documentación, sumado a lo simple de su configuración inicial, hace olvidar antiguos dolores de cabeza a la hora de automatizar tests. Si algo separa a Cypress de otras herramientas de testing es que parece tener todo resuelto.
El problema del responsive testing es que se presenta como una zona poco explorada y explotada: los ejemplos de la documentación se acercan más a una demo que a un proyecto real.
Otra cosa que me llamó la atención es que, buscando en la web, no encontré más que un artículo de Suman Kunwar (sobre el cual volveré a lo largo de este texto) que se acercó mucho a lo que yo tenía en mente.
Con este contexto acerco mi contribución, una mirada un poco más profundo y enfocada en aplicar el responsive testing en proyectos de mayor escala.
Algunas cuestiones que tuve en cuenta para pensar el problema
No puedo usar el enfoque de la documentación de Cypress porque sugieren que en cada test se indique la resolución a usar (pueden ser N resoluciones en un forEach por test).
Me imaginé varias suites de tests y la inviabilidad de este enfoque era evidente.
Si no quiero usar el if que figura en el ejemplo anterior tengo que usar: o siempre resoluciones con value pairs o los presets de Cypress (lo cual acota mucho las posibilidades).
¿Es viable que en cada corrida de la/s suite/s, cada test se ejecute en todas las resoluciones? ¿Qué pasa si necesito hacer una regresión de un bug sólo en una resolución? En esta cuestión, además, decidí que lo mejor sería tener una resolución default con la que testee todo el tiempo, y usar luego un set de N resoluciones para hacer pruebas integrales.
En escena también aparecen los userAgents, de los cuáles nunca se habla en la documentación pero sí se mencionan en el artículo arriba citado. Como podrán observar en el artículo hay dos cosas importantes:
- Las opciones que propone son ‘mobile’, ‘tablet’, ‘desktop’. Asocia una resolución (iPhone5) con un único userAgent. La pregunta es obvia: ¿siempre voy a querer testear iPhone5 con ese userAgent? ¿siempre voy a usar la resolución de un sólo dispositivo para testear mobile? ¿cada vez que necesite usar otro dispositivo voy a tener que editar esos valores? La cuestión siempre recae en que no es escalable.
- La solución parece algo estática; determino el tipo de dispositivo y sólo tengo una opción. Me imagino el movimiento normal de los proyectos, en los que las definiciones cambian con frecuencia, y veo que sería difícil que la solución se acople fácilmente a esos cambios.
Entonces, ¿cómo logramos tener una solución responsive escalable?
Para eso vamos a utilizar la API de Cypress a través de un script.
Como en todas las cosas, esta propuesta no es más que eso: una propuesta. No pretende ser la solución última. Pero, luego de haberla puesto a prueba, creo se convirtió en un sólido punto de partida.
Lo que hizo muy bien el artículo citado previamente fue entender que el camino es tener una configuración default del cypress.json, y que ésta modifique algunos de sus settings según los parámetros que le indiquemos. Acá es donde nos diferenciamos y expandimos la solución. Quiero tener N resoluciones (es decir, N dispositivos disponibles para simular) y N userAgents (en otras palabras, N sistemas operativos simulados). Es más: quiero poder diferenciar iOS de Android. Quiero poder ofrecer corridas y regresiones cada vez más específicas: iphone-X, iOS 13.3.1.
Commands
Lo que nos va a permitir el script será pasarle como parámetro <device> y <osVersion> y en el caso de ser necesario (por ejemplo, si corremos el script en integración continua) podemos pasar el parámetro <record> el cuál seteará la record key almacenada como variable de ambiente. También permite pasar el parámetro <open> para poder utilizar el runner de Cypress.
Hands on!
La lógica del script permite llegar siempre a la ejecución de las suites. Siempre que el parámetro <device> exista en el archivo devices.js, el script va a esperar que le pasemos el <osVersion> correspondiente a ese tipo de device (iOS & Android). Por ejemplo, ingresamos: iphoneX
Una vez que el device está definido, el script buscará dentro de los osVersions definidos en el archivo os.js que correspondan a los dispositivos iOS. Por ejemplo, ingresamos: “13.3.1”
Si la versión ingresada no existiera en la lista, el script irá a buscar la versión default de ese OS.
node cy-start.js — -d iphoneX -osV 13.3.1 -o
Desde el runner de Cypress podemos ver que tenemos disponibilizadas como variables de ambiente: device, osVersion, osType.
Podemos aprovechar esas variables para usarlas en los nombres de las suites.
Al correr el script del ejemplo vamos a ver que los tests corrieron en la resolución y versión que especificamos
Por último, como ayuda, le disponibilizamos al usuario no sólo de qué formas puede ejecutar el script, sino también cuáles son los dispositivos y versiones que tiene disponibles.
El script completo acá.
Conclusión
Gracias a este enfoque, ahora somos capaces de testear funcionalidades o regresionar bugs en dispositivos y versiones específicas, pudiendo adaptarnos a cualquier combinación necesaria. También nos permite acortar los tiempos del testing diario utilizando versiones seteadas por default. La lógica del script permite agregar cuantas configuraciones y variaciones necesitemos para mejorar nuestros tests y que se adapten a nuestras necesidades de testing.