Creando un API REST con swagger Node (parte 6)

Haciendo pruebas unitarias con mocha, chai, sinon y supertest

Diego Pérez Molinero
6 min readMar 30, 2018

En esta parte veremos como realizar pruebas unitarias contra el código que tenemos hasta ahora desarrollado.

Pruebas con mocha y chai

Para probar el api, nos basamos como pilar fundamental de las pruebas en el conocido framework de testing de javascript mocha.

Aquí tenéis la página principal del framework: mocha

La idea principal es usar describe como agrupador de los test que realizamos e it para lanzar el test.

Aquí vemos un ejemplo sencillo, usando la librería assert para las aserciones:

var assert = require('assert');
describe('Array', function() {
describe('#indexOf()', function() {
it('should return -1 when the value is not present', function() {
assert.equal([1,2,3].indexOf(4), -1);
});
});
});

Podemos anidar tantos describes como queramos en los tests.

Para las aserciones, se puede usar una serie de librerías (should, assert, chai, etc…)

Nosotros usaremos chai que incluye la funcionalidad de assert, expect y should, que implica una forma distinta de expresar la aserción según nos convenga, anque tiene sus particularidades.

Recomiendo echar un vistazo en profundidad a chai, para comprender mejor el uso de las tres formas aquí: guía de aserciones de chai

Spies y Stubs con sinon

Muchas veces cuando estamos probando una función de nuestro código, esta función acaba llamando a una o varias funciones, que no queremos probar en este momento del test, bien porque ya las probamos en un test específico que recoge dichas pruebas o bien porque realmente, sólo queremos probar nuestra función, y lo que necesitamos hacer es simular la ejecución de estas funciones.

Sinon es un framework que nos permite simular una llamada a una función, sustituyéndola por una “falsa” función, permitiéndonos obviar la llamada, simular que la hemos llamado y devolver lo que queramos, o bien, que está nueva falsa función que sustituye a la verdadera, devuelva unos resultados ante ciertos parámetros de entrada determinados.

Si queremos simplemente saber si es llamada con unos parámetros determinados o un número de veces, nos interesará usar un espía (spy).

Otras veces, queremos obtener un resultado concreto en la llamada a la falsa función, para lo que usaremos un stub. El stub se usa para verificar el estado de los objetos.

En otros escenarios, nos va a interesar realizar un mock de la función, ya que nos interesa probar el comportamiento de los objetos. Por ejemplo, si la función llama a otras funciones. Podemos realizar un mock de un módulo completo con proxyquire, sustituyendolo por un módulo falso que nos va a facilitar mucho el testeo.

Supertest para probar los métodos del API en los controladores

La librería supertest nos permite probar fácilmente las llamadas http a nuestros métodos de los controladores a los que enlaza Swagger Node.

Cómo aplicamos todo esto a nuestro código

Vamos a ir viendo en detalle cómo implementamos los test unitarios en cada una de las capas que tenemos en la aplicación.

Scripts

En nuestro fichero package.json usaremos los siguientes scripts para probar el código:

Usamos test y cover como scripts principales para probar

Usaremos los siguientes scripts:

  • test: para lanzar todos los test
$ npm run test
Resultado que obtenemos de ejecutar todos los test
  • cover: lanzamos los test y además obtenemos el porcentaje de cobertura de código de nuestros test.
$ npm run cover
Resultado de la cobertura de nuestros test

Sonarqube

Si tenemos levantado nuestro sonarqube (ver capítulos anteriores de cómo hacerlo), podemos también ejecutar el script de sonar-scanner para poder consultar los resultados de nuestros tests:

$ sonar-scanner
Resultados de los test en sonarqube

Veamos ahora en detalle en cada una de las capas de la aplicación como hemos ido resolviendo los test en cada caso.

Controladores

Inicialmente declaramos todos los módulos principales de testing que usamos y los módulos que vamos a necesitar.

Usamos los bloques before (que se ejecuta antes de empezar todos los test) para inicializar el servidor y enlazarlo con supertest.

Principio del test del Gamesystem Controller

A continuación empezamos a realizar los test siguiendo este patrón:

Nuestro primer test contra el API

Creamos un stub para simular la llamada al método getGameSystems del servicio gamesystemService, y le decimos lo que queremos devolver, que en este caso, lo hemos definido en el módulo de expectations (un módulo donde agrupamos una serie de valores esperados por los test).

Usamos un módulo de expectations para almacenar los valores esperados

Con esto lo que estamos haciendo es sustituir la llamada real por la llamada a nuestro stub e indicándole lo que queremos obtener.

En la parte del it hemos indicado el path http al que llamamos, con los parámetros adecuados y lo que esperamos obtener, que es lo que nos ha devuelto la llamada al servicio que hemos “stubbeado”, incluído el código http de respuesta que en este caso es 200.

Viendo ahora un caso donde esperamos obtener un error 500:

En este caso, hemos forzado el fallo en el Stub y con supertest esperamos que nos devuelva un error

Servicios

En los servicios se sigue la misma filosofía, aunque aquí no usamos supertest. Usamos simplemente sinon para stubbear lo que necesitemos y chai para las aserciones. Aquí lo que stubbeamos son o bien llamadas a repositorios (que están por debajo de la capa de servicio) o bien llamadas a otras funciones de otros servicios o del mismo servicio.

Aquí stubbeando una llamada al repositorio de Gamesystems

Antes de seguir testeando, es importante tener en cuenta una serie de consideraciones:

  • No deberíamos tener referencias circulares, es decir dos servicios no deberían requerirse mutuamente (referencia circular). Dificulta mucho el testeo unitario y refleja una mala arquitectura de la aplicación.
  • Si necesitamos stubbear un método del mismo módulo que estamos testeando, esta función ha de llamarse utilizando como prefijo module.exports y ha de estar recogida por tanto dentro de las funciones de module.exports que exportamos.
  • Para poder testear una función, es necesario exportarla, ya que mocha necesita acceder a ella. Aunque existen alternativas a esto, de momento vamos a hacerlo así para mayor sencillez…aunque es extraño que convirtamos las funciones privadas no exportadas del módulo en funciones públicas. De momento, lo anotamos con la etiqueta // for testing donde lo hemos hecho.
Marcamos las funciones privadas con //for testing en el module.exports para evitar confusión

Repositorios

Para el testing de repositorios, que es la capa más profunda de nuestra aplicación, aplicamos lo mismo que hemos hecho en la capa de servicios.

Testing de los repositorios

En nuestro caso, tenemos los dos repositorios totalmente desacoplados, y no dependen de ningún otro módulo que haya que stubbear, así que queda mucho más sencillo probar esta última capa de nuestra aplicación.

Continuar a la septima parte del tutorial

finalmente, dejo como siempre el enlace al repositorio de github para poder descargar el código completo.

--

--

Diego Pérez Molinero

Software Architect & defender of clean architecture and domain driven design. Supporter of infrastructure & devops as code into the projects.