Cómo testear el frontend de una aplicación web

Parece que hemos decidido que la mejor forma de hacer aplicaciones es utilizando HTML y Javascript, incluso si se trata de aplicaciones de escritorio que consumen cientos de megas de RAM para realizar las tareas más básicas. Sin entrar a discutir la adecuación de tecnología a caso de uso, lo que sí está claro es que cuanto más compleja sea la parte cliente de nuestras aplicaciones web, más importante es diseñar una estrategia de testing adecuada que nos permita mantener una calidad razonable para poder dormir tranquilos.

Cuando decides ponerte a testear este tipo de aplicaciones es normal que surjan un montón de dudas. Siempre están las clásicas consideraciones sobre qué tipo de tests utilizar en cada caso, pero es que además nos encontramos con la dificultad de elegir entre decenas de librerías de testing y de escoger sobre qué plataforma o plataformas vamos a ejecutar los tests.

En este posts vamos a intentar obtener una visión global que nos permita saber qué terreno pisamos. No habrá código y ni siquiera profundizaremos en ninguna librería, pero debería servir cómo base para empezar a diseñar tu propia estrategía de testing en función de tus necesidades.

Muchas plataformas diferentes

Una de las cosas en las que más se diferencia testear un cliente web de otro tipo de aplicaciones, es en la diversidad de plataformas que tenemos para poder ejecutar los tests.

En la parte de backend, lo normal es que ni siquiera te plantees en qué plataforma vas a ejecutar los tests. Si estás trabajando con .NET, ejecutarás los tests en el framework sobre el que vas a desplegar tu aplicación, y poco más (otro caso es si se trata de una librería, en cuyo caso puede que te animes a testearla en varias versiones del framework).

Sin embargo, cuando queremos testear código Javascript, tenemos varias opciones.

OJO: En Javascript hay ocasiones en las que se diferencia entre el runner que ejecuta los tests, la librería con la que se escriben los tests, e incluso la librería que se utiliza para los asserts. No te sorprendas si tienes que juntar varias piezas para que todo funcione como te gustaría.

Podemos testear nuestro código ejecutándolo en node.js, por ejemplo utilizando mocha.js o jasmine. Si estamos escribiendo tests que no requieren acceso al DOM ni a APIs del navegador, es una alternativa muy buena porque son tests fáciles de montar y rápidos de ejecutar.

Como contrapartida, sólo estaremos testeando el comportamiento sobre V8 (el motor de javascript usado por node.js y Chrome, entre otros), con lo que teóricamente podríamos encontrar diferencias de comportamiento con respecto a otros motores de javascript. En general si no estamos tocando DOM ni APIs del navegador esto no suele ser un problema, pero hay algunas cosas concretas (como por ejemplo el parsing de fechas) que sí son dependientes del navegador y podrían llegar a darnos alguna sorpresa.

Teóricamente podríamos usar esta técnica para escribir tests que tocaran DOM o APIs del navegadores utilizando los polyfills adecuados (por ejemplo, jsdom, pero aquí sí que empezamos a pisar un terreno algo más resbaladizo y es más fácil que encontremos pequeñas diferencias entre navegadores. Pese a ello, mezcladas con librerías como enzyme, una herramienta específica para ReactJS, permiten cubrir algunos escenarios interesantes.

Otra alternativa es ejecutar los tests en un navegador de verdad. De hecho, si vas por esta vía probablemente deberías ejecutarlos en varios navegadores de verdad. Para ello puedes utilizar librerías de testing como las que indicaba antes (mocha, jasmine) y apoyarte en algún lanzador tipo karma que te permita ejecutar fácilmente los test en los navegadores.

La ventaja de estos tests, como te puedes imaginar, es que son mucho más fieles a la realidad y puedes comprobar si ese código tan bonito que has escrito funciona en Internet Explorer (probablemente no) y cuántos polyfills necesitas para hacerlo funcionar en Safari.

Montarlos es un poco más complicado y ejecutarlos es bastante más lento porque hay que arrancar el navegador y, aunque los dejes en modo watch, siguen tardando un poco más que los tests en node.js.

Dentro de este estilo, a veces es cómodo utilizar un phantom.js, un navegador basado en webkit que no tiene interfaz de usuario y que se puede controlar completamente a través de código, lo que permite ejecutar fácilmente tests en él.

Existe otra forma de ejecutar los tests en un navegador: interactuando directamente con el interfaz de usuario. La diferencia es que antes ejercitábamos nuestro código invocando funciones concretas (más en la línea de tests unitarios), y ahora tocamos botones, pulsamos enlaces e introducimos textos en inputs.

Para estos tests de integración la herramienta más popular es Selenium, que en javascript podemos utilizar a través de wrappers como protractor si usamos AngularJS o, de forma más general, nightwatch.

Este tipo de tests es el más lento de todos, pero también es el que nos permite probar con mayor fidelidad el interfaz de usuario, por lo que a veces merece la pena utilizarlo.

Una posible estrategia de testing

Una vez vistas las opciones que tenemos para ejecutar tests y sus características, sólo nos queda decidir cuáles utilizar. En esto, como siempre, el contexto es fundamental y no hay dos aplicaciones iguales, pero sí hay una serie de factores que nos pueden servir de guía.

Cuanto más fácil sea escribir los tests, mejor. Y cuanto más rápido se ejecuten, también mejor. Eso nos lleva a que deberíamos intentar aislar al máximo posible nuestra lógica en tests que podamos ejecutar sobre node.js. Para ello podemos valernos de las técnicas habituales para eliminar dependencias de los tests y aislarlos al máximo, ya no sólo del navegador, sino también de la librería o framework que estemos utilizando.

En los casos en los que necesitamos interactuar con el DOM o APIs del navegador, podríamos recurrir a utilizar lanzadores tipo karma, pero a menos que estés desarrollando una librería de componentes gráficos reutilizables, es probable que te lo puedas ahorras.

Normalmente es fácil cubrir «la miga» de la aplicación con tests unitarios de node.js, y dejar el resto para tests de integración usando Selenium y un entorno lo más parecido posible a producción.

De todas formas, como decía antes, esto es muy dependiente de la aplicación y se aplican las ideas generales sobre tipos de tests.

Testeando lenguajes transpilados a Javascript

Teniendo en cuenta la cantidad de lenguajes que se compilan a Javascript y la adopción cada vez mayor que van teniendo, es cada vez más frecuente que la aplicación que vamos a testear no esté escrita directamente en Javascript, sino en TypeScript, Kotlin, F# o PureScript (por citar unos cuantos).

En estos casos tenemos básicamente dos alternativas: integrar la parte de testing y compilación, o compilar primero a Javascript y luego testear como si fuese una aplicación Javascript normal.

Integrando la parte de compilación dentro del proceso de testing tendremos una experiencia más cómoda y ágil, sobre todo si estamos escribiendo tests unitarios para ejecutar en node.js. Además, podremos escribir los tests en el mismo lenguaje que el código y usar librerías idiomáticas para el lenguaje y podremos los errores y stacktraces referidos al código original, no al código transpilado (que en algunos casos puede ser extremadamente diferente).

La única pega es que puede ser un poco más complicado montar el proceso de compilación+testing, pero hoy en día casi todos los lenguajes tienen previsto este escenario.

Donde sí puede tener más sentido primero compilar y luego ejecutar los tests contra el código Javascript es en los tests de integración con Selenium. Puede que el lenguaje que hayamos elegido no tenga runners tan maduros como los de Javascript y además es una forma de asegurarnos de que estamos testeando contra un entorno lo más parecido a producción posible.

Conclusión

Testear el frontend de una aplicación web puede parece una tarea complicada, porque se une la dificultad propia de establecer una estrategia de testing adecuada, con la variedad de librerías existentes para testearlas, y el añadido de tener distintas plataformas para ejecutar los tests.

No obstante, una vez que se tienen claras unas bases mínimas, es perfectamente viable montar un sistema de testing muy completo, en el que aprovechando las características de los distintos tipos de tests, librerías y lanzadores, podamos garantizar una calidad razonable de la aplicación y utilizar un flujo de trabajo muy productivo.

Al igual que pasa en el resto de aplicaciones, intenta encapsular todo lo que puedas en tests unitarios que no dependan de APIs externas (DOM o navegador) y que, por tanto, puedas ejecutar sin problema en node.js.

Sobre eso montar una capa de tests de integración que interactúen con el navegador como si fueran un usuario real da un extra de confianza que viene muy bien.

3 comentarios en “Cómo testear el frontend de una aplicación web

  1. Personalmente, el engorro que le veo al «testing» en «front-end» es a la hora de depurar pruebas. Si el lenguaje utilizado en las pruebas requiere ser transpilado la depuración no se realizará sobre el código original sino sobre la transpilación.

    Un caso típico: pruebas en ES6 y ejecutadas con Mocha. La depuración corre sobre Chrome y se muestra código ES5.

  2. ¡Muchas gracias! No se me había ocurrido que Mocha contemplara un parámetro de estas características, asociaba el problema más bien al navegador.

Comentarios cerrados.