Angular es un framework bastante completo, no viene solo con librerías para desarrollar aplicaciones ordenadas y visualmente atractivas, si no que también incluye las herramientas necesarias para armar una buena batería de tests para tus proyectos.

En este posts ahondaremos en las herramientas de testing que vienen incluidas en Angular, cuales son, para que sirven y finalmente como utilizarlas. Además veremos como se organizan los distintos tipos de test dentro de un proyecto Angular.

Tipos de tests

Antes de comenzar, identificaremos los 3 tipos distintos de testing que podemos lograr con las herramientas que nos da Angular:

  • Test unitarios
    • Tests orientados a probar una funcionalidad específica de tu proyecto. Deben estar lo más aislado posibles, no deben interactuar con servicios externos e idealmente deben abarcar solo una función.
  • Tests de integración
    • Orientados a probar un flujo de tu proyecto: Se ejecuta una prueba para validar el comportamiento de múltiples rutinas de tu proyecto trabajando en conjunto.
  • Tests de extremo a extremo (e2e)
    • Son tests que se ejecutan sobre tu proyecto andando: Se simulan las acciones de un usuario real desde un navegador, como el ingreso de formularios, presionar botones, comprobar mensajes de respuesta, etc. Como puedes ver estas pruebas son las más completas ya que contemplan el flujo que va desde la pantalla hasta el backend y de vuelta hacia atrás.

Tipos de herramientas de testing

Angular incluye varías librerías para desarrollar los tests de tu proyecto. Antes de entrar en detalle, entenderemos los distintos tipos de herramientas que existen para llevar a cabo esta tarea:

  • Test runner
    • Es la herramienta que se encarga de identificar y correr los tests definidos en tu proyecto.
  • Assertion library
    • Cuando defines un test, también defines una condición que se debe cumplir para marcar ese test como aprobado. Si deseas testear la lógica de la función hola(), puedes invocarla desde tu test y mediante assertions validar que su retorno sea siempre igual a 'mundo'. Las librerías de assertions son básicamente un set de funciones para validar el cumplimiento de tus tests de forma elegante.
  • Syntax
    • Este es el componente que te permite escribir tus tests de forma ordenada y modular, esto permite que puedas escribir tests que sean fáciles de leer. Incluso existen algunas librerías como Gherkin de Cucumber que te permiten escribir tests en lenguaje humano.
  • Web driver
    • Es el componente que utilizas cuando quieres ejecutar pruebas sobre tu aplicación web desde un navegador. Te permite realizar flujos de usuario automatizando clicks, ingreso de textos, scrolling, etc. y además te permite leer los mensajes que se muestran por pantalla.

En otros lados podemos encontrar que a ciertas herramientas se les llama test frameworks o test environments. Esto no vendría siendo mas que un set integrado de herramientas de los distintos tipos que recién hemos mencionado.

Herramientas de testing en Angular

Ahora que ya entendemos los tipos de herramientas de testing que existe, veremos las herramientas que trae Angular y las asociaremos.

  • Karma
    • Karma es el test runner de Angular. Cuando corres los tests de Angular estás ejecutando Karma que en el fondo lo que hace es identificar todos los archivos de test del proyecto y correrlos uno a uno de forma secuencial. En Karma tu puedes definir el output del proceso: el detalle de salida que necesitas, salida en reportes, etc. Es extensible mediante plugins, lo que te amplía aun más las posibilidades. Karma se utiliza para la ejecución de tests unitarios en conjunto con Jasmine, del cual utiliza las assertions y la sintaxis.
  • Protractor
    • Ahora pasamos a los tests e2e. Protractor además de ser un test runner, nos provee de un set de funciones para interactuar con los elementos visuales de aplicaciones Angular mediante un Web driver: hacer clicks, ingresar input texts, capturar información desplegada en pantalla, etc. Protractor actuá como un test runner de tests e2e especializado para Angular, y por debajo utiliza Jasmine para lo que es assertions, sintaxis y test reporting.
  • Jasmine
    • Jasmine es un test framework. Si bien incluye un test runner, en Angular se utiliza como librería de assertions, sintaxis y reporting. En el fondo Jasmine es quien te permite escribir tus tests de forma organizada y con una sintaxis legible. Se utiliza tanto para tests unitarios (desde Karma) como para tests e2e (desde Protractor).

En resumen, tenemos que Angular viene configurado para test unitarios con Karma, y con Protractor para tests e2e. Si bien son 2 tipos de tests distintos, ambos aprovechan las capacidades de Jasmine quien provee las funciones de assertions, sintaxis y test reporting.

Pero nos falta algo! Los tests de integración. Si bien son un tipo distinto de tests, dentro del contexto de herramientas caen junto a los tests unitarios, ya que no requieren una herramienta especializada para su ejecución.

Ejecutar y configurar tests unitarios

Los tests vienen configurados de tal forma que sean fáciles de correr. Partamos inicializando un nuevo proyecto en Angular.

Si no tienes instalada la CLI de Angular, ejecutar:

npm install -g @angular/cli

Ahora, crea un nuevo proyecto

ng new testingApp
cd testingApp

Dado que el proyecto base viene con tests definidos, podemos ejecutar inmediatamente los tests con el siguiente comando:

ng test

Si todo sale bien, podrás ver el detalle de los tests ejecutados en una ventana de Chrome:

Si estás en Linux, posiblemente deberás crear una variable de entorno para reconocer Chrome: export CHROME_BIN=chromium-browser

Si continúas haciendo cambios sobre tus fuentes, los tests se ejecutarán continuamente mediante autowatch.

Pero si queremos ver la salida exclusivamente por consola (para CI por ej.) necesitamos hacer algunos ajustes.

Primero, nos instalamos el karma spec reporter para visualizar el detalle de tests por consola:

npm install karma-spec-reporter --save-dev

Luego, hacemos unas configuraciones en el archivo karma.conf.js.

En el arreglo plugins, agregamos el nuevo reporter:

require('karma-spec-reporter'),

Y por último, actualizamos los siguientes 4 atributos:

    reporters: ['spec'],
    autoWatch: false,
    browsers: ['ChromeHeadless'],
    singleRun: true,

Con esto ya tendremos una salida más bonita y apta para integración continua:

Ejecutar y configurar tests e2e

Para ejecutar los tests e2e, solo correr el comando:

ng e2e

Esto ejecutará una instancia de Chrome para correr los tests visualmente. El output será por terminal:

Si queremos que el Chrome se ejecute en modo headless para agregarlo a pipelines, configurar el archivo e2e/protractor.conf.js. capabilities debe quedar así:

  capabilities: {
    'browserName': 'chrome',
    chromeOptions: {
      args: ["--headless"]
    }
  },

Si vuelves a ejecutar ng e2e verás que ya no se levanta la ventana de Chrome.

Organización de tests dentro del proyecto

Otro de los puntos importantes que Angular maneja por nosotros es la organización de los tests dentro del proyecto.

Algunas de las utilidades de la CLI de Angular son comandos para crear componentes y servicios como:

ng generate component NuevoComponente
ng generate service NuevoServicio

Esto generará los fuentes de un nuevo componente o servicio en tu proyecto. Además también generará un fuente de test con el sufijo .spec.ts:

Este archivo spec está escrito con la sintaxis de Jasmine y contiene todos los test para un componente en particular.

A medida que vayas agregando lógica a tus proyecto debes preocuparte de que los spec también vayan creciendo agregando pruebas sobre las funciones que se vayan incorporando.

Cuando ejecutes ng test, Karma identificará de forma automática estos ficheros spec y los irá ejecutando uno a uno.

Los tests e2e se organizan de un modo distinto, estos van todos en la carpeta e2e en la raíz de tu proyecto.

Dado que los tests e2e son visuales, no los organizamos según los componentes internos de la aplicación si no de acuerdo a las pantallas que tenga nuestra aplicación.

Por ejemplo, si tenemos una pantalla de login y otra es un dashboard con información, deberíamos tener algo así en nuestro tree:

Ahora, porque vemos 2 archivos por cada pantalla?

  • .e2e-spec.ts
    • Es el archivo de test escrito con la sintaxis de Jasmine, es básicamente lo mismo que los .spec.ts de los test unitarios. La diferencia es que hace uso de un PageObject descrito a continuación.
  • .po.ts
    • Es el PageObject, básicamente corresponde a un patrón para la creación de tests e2e. Este archivo contiene la lógica de acceso a los elementos visuales de cada pantalla. Por ejemplo, el ingreso de un formulario completo puede encapsularse dentro del .po.ts.

La gracia del PageObject es que nos permite mantener specs mucho más legibles, al encapsular la lógica de acceso a elementos visuales en un archivo distinto, en el spec solo escribimos la lógica pura de nuestro test.

Final notes

Por lo general, para escribir tests en nuestros desarrollos debemos invertir tiempo en elegir e implementar todas las librerías necesarias. Cuando usamos Angular estamos 100% cubiertos, por lo que solo debemos preocuparnos de realizar un par de ajustes y echar a correr los tests, lo cual es una gran ventaja frente a cualquier otro framework.

Es este post se entregó una introducción a las herramientas de testing en Angular, por lo que si bien no estaba contemplado entrar en detalles más técnicos como la codificación de los tests mismos, planeo añadir otra entrada en el futuro abarcando estos temas.

Por último, considerar que este post se redactó usando Angular 7, con Angular 8 ya a la vuelta de la esquina tirando sus últimas betas.