Mocha, Chai y Sinon Js

“¿Unit testing?, ¿cómo crees?, ¡el código es perfecto!”

Ivan Díaz
Nowports Tech and Product
6 min readJan 11, 2021

--

Esta frase es la primera señal de que tu aplicación necesita tener unit testing.

La mayoría de los lenguajes de programación cuentan con frameworks específicos para realizar unit testing, a grandes rasgos estos se encargan de revisar el código que escribiste y que funcione en base a los casos de prueba.

En esta ocasión, no vamos a adentrar tanto a la teoría de unit testing. Si desean saber más, pueden darle una revisada al artículo de Diego Ponce, donde explica más a detalle el mundo del testing.

Quiero mostrarles unos ejemplos con tecnologías que he estado aprendiendo recientemente, así que espero sea de gran aporte.

Los ingredientes que vamos a necesitar son:

Mongoose

Es una librería creada en Node.js para trabajar con MongoDB, sistema de base de datos que vamos a usar (para el propósito de este tutorial, esto es irrelevante).

Rewire

Rewire agrega un setter y getter especial a los módulos, para que pueda modificar su comportamiento y una mejor prueba unitaria.

SuperTest

Módulo de node que nos provee un nivel alto de abstracción para probar servicios http.

Para instalar SuperTest:

Mocha js

Un framework basado en Node js que, además de ser popular, es fácil de usar y muy flexible al combinarlo con otras librerías que ayudan a multiplicar su eficiencia; algo genial es que te entrega reportes muy detallados de tus pruebas.

Para instalar Mocha:

Chai js

Podríamos usar assert el módulo de node, pero está limitado si lo comparamos con Chai, ya que esta cuenta con comprobaciones avanzadas. Tiene varias interfaces: assert, expect y should.

Chai permite elegir un estilo que resulte más legible a la hora de desarrollar unit testing. Otra virtud es que es expansible: tiene muchos complementos para cualquier cosa; por ejemplo, chai-as-promised, un plugin para agregar promesas en el código de las pruebas, o sinon-chai, para crear una mancuerna con Sinonjs.

Para instalar Chai:

Sinon js

Probar código donde involucre consultas, a base de datos por ejemplo, significa que se tiene que configurar un entorno donde las pruebas puedan ejecutarse con éxito y esto puede llegar a ser un dolor de cabeza.

Para eso nos apoyaremos de Sinon js, que cuenta con Spies, Stubs y Mocks. Estos se consideran como dobles de prueba, igual que los dobles de actores de película que hacen el trabajo peligroso. En este caso, usaremos Stubs.

Si deseas saber más sobre Sinon js, puedes revisar la documentación.

Un Stub y la función a probar

Bien, vamos a crear las pruebas unitarias de un CRUD en una aplicación con express js, agregaremos un middleware para hacer algo más realista el ejercicio y veremos cómo realizar sus pruebas.

Lo primero que tenemos que hacer es crear nuestra aplicación con:

Seguido de esto es necesario instalar las librerías mencionadas (Mocha, Chai, Sinon Js).

Ya realizado las instalaciones, vamos a crear la estructura del proyecto. Agregaremos un modelo llamado User, una carpeta con las acciones en la BD y su configuración, un archivo app.js, donde están declarados los endpoints, y un archivo llamado test.js donde escribiremos las pruebas.

Se verá algo así:

Al final del artículo dejaré un enlace al código que he creado.

Ahora comencemos con la prueba del primer endpoint de nuestra aplicación, el cual va crear un nuevo usuario.

Test / POST User

Analicemos lo que pasa aquí.

Al inicio todo es normal: llamamos las herramientas necesarias, declaramos algunas variables y todo bien.

Hay una constante en particular llamada ‘sandbox’, que tiene como valor sinon.sandbox.create(). Esta instancia nos crea una fábrica de Spies, Stubs y Mocks, para usar todos los que necesitemos.

Luego inicia un bloque de código con describe. Como su nombre lo dice, esta función describe la prueba que haremos. En el primer parámetro que recibe, generalmente se agrega el nombre del archivo que está probando y en segundo agregamos una función donde vamos a declarar el contenido de la prueba.

Dentro de describe agregamos context para declarar la prueba de una funcionalidad en especifico, ademas que nos ayudará a leer fácilmente el código. Por convención, es la función que se prueba y de igual manera lleva dos parámetros: el primero es un string donde se agrega la descripción y el segundo es una función en la que declaramos los casos de prueba.

afterEach y beforeEach

Mocha nos pone a disposición algunos hooks. En mi caso declaré afterEach y beforeEach: funciones que pueden hacer que se ejecute alguna instrucción que le demos antes y después de comenzar la prueba. Esto nos ayuda a preparar algunas configuraciones específicas, a crear instancias o cualquier cosa que imaginemos.

Usaremos el hook afterEach que se encargará de restaurar nuestra sandbox, ya que nos puede dar problemas si acumulamos Stubs. Recuerda que cada uno tiene una configuración personalizada y así nos aseguramos de que cada prueba tenga un nuevo comienzo.

En el hook beforeEach vamos crear un Stub para el middleware del endpoint, que se encargará de validar la autenticación:

Dentro del context ‘Post / user’ tenemos el caso de prueba ‘should call user.create’, en el cual daremos instrucciones a nuestro Stub de la siguiente manera:

El primer parámetro indica el objeto que vamos a reemplazar y el segundo indica el método que ejecutará. Al resolverse, regresará un objeto que simula la promesa resuelta, en este caso el usuario que se agregó a la base de datos.

Una ventaja es que nos permitirá saber con qué estamos trabajando durante nuestras pruebas y así sabremos que estamos buscando en lugar de tener datos que tal vez no existan.

Luego, usando superTest realizamos la petición al endpoint Post/user, agregando las aserciones por el plugin de sinon-chai que realmente son muy intuitivas. Validamos que el Stub sea llamado y que las propiedades de la respuesta sean las esperadas.

Ahora agregaremos un caso de prueba con un error esperado…

Para este caso vamos a crear un nuevo Stub que ocasione que la acción de crear usuario retorne un error. También necesitamos otro Stub que haga una petición falsa, que responda con un error 400 y su mensaje de error.

Después de esto, vamos a obtener la función privada handleError gracias a rewire y le pasaremos como parámetro nuestro pequeño impostor errorStub.

Por último, hacemos el request con los resultados esperados y ejecutamos las pruebas con el siguiente comando:

Deberíamos ver en nuestra terminal de comandos un resultado así, donde nos indica el resultado de las pruebas.

En absoluto, el uso de unit tests nos ayuda a tener un funcionamiento sólido de nuestro código, volviendo mínimas las probabilidades de que se nos rompa algo. Espero que este artículo te anime agregar unit test en tus proyectos, si es que aún no lo haces.

Por último, quiero compartir este breve texto de un artículo de Michael Feathers que encontré durante mi curva de aprendizaje y vale la pena analizarlo:

Una prueba no es una prueba unitaria si:

- Habla con la base de datos

- Se comunica a través de la red

- Toca el sistema de archivos

- No se puede ejecutar al mismo tiempo que cualquiera de sus otras pruebas unitarias

- Tienes que hacer cosas especiales en tu entorno (como editar archivos de configuración) para ejecutarlo.

👉 https://github.com/owivans/testingMocha

¡Nos leemos luego!

--

--

Ivan Díaz
Nowports Tech and Product

Senior Software Engineer at Nowports | Program Lead & JavaScript Mentor at Kodemia