Prueba de código Node.js con Mocha y Chai

Escribir pruebas unitarias es una de esas cosas que muchas personas olvidan hacer o simplemente evitan por completo, pero cuando las tienes son salvavidas. Desarrollo basado en pruebas...

Escribir pruebas unitarias es una de esas cosas que mucha gente olvida hacer o simplemente evita por completo, pero cuando las tienes son salvavidas.

Desarrollo basado en pruebas, lo que significa que escribes tus pruebas antes que tu código, es un gran objetivo por el que luchar, pero requiere disciplina y planificación cuando\ estas programando Para que todo este proceso sea mucho más fácil, necesita marcos de prueba y aserción potentes y fáciles de usar, que es exactamente lo que Moca y [Chai](http://chaijs .com/) son.

En este artículo, le presentaré estas dos bibliotecas y le mostraré cómo usarlas juntas para crear rápidamente pruebas unitarias legibles y funcionales.

Chai

Chai es una biblioteca de aserciones que proporciona tanto BDD como TDD estilos de programación para probar su código en cualquier marco de prueba.

A lo largo de este artículo, nos centraremos en el estilo BDD usando la interfaz expect de Chai.

expect utiliza una API de lenguaje más natural para escribir sus afirmaciones, lo que hará que sus pruebas sean más fáciles de escribir y mejorar más adelante. Esto se hace encadenando captadores para crear y ejecutar la aserción, lo que facilita la traducción de los requisitos al código:

1
2
3
4
5
var user = {name: 'Scott'};

// Requirement: The object 'user' should have the property 'name'

expect(user).to.have.property('name');

Algunos ejemplos más de estos captadores son:

  • a
  • ser
  • es
  • y
  • tiene
  • tener

Bastantes de estos captadores se pueden encadenar y utilizar con métodos de aserción como true, ok, exist y empty para crear algunas aserciones complejas en una sola línea. Algunos ejemplos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
"use strict";

var expect = require('chai').expect;

// Simple assertions
expect({}).to.exist;
expect(26).to.equal(26);
expect(false).to.be.false;
expect('hello').to.be.string;

// Modifiers ('not')
expect([1, 2, 3]).to.not.be.empty;

// Complex chains
expect([1, 2, 3]).to.have.length.of.at.least(3);

Una lista completa de los métodos disponibles se puede encontrar aquí.

También puede consultar la lista de complementos disponibles para Chai. Esto hace que sea mucho más fácil probar funciones más complejas.

Tome chai-http por ejemplo, que es un complemento que lo ayuda a probar las rutas del servidor.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
"use strict";

var chai = require('chai');
var chaiHttp = require('chai-http');

chai.use(chaiHttp);

chai.request(app)
    .put('/api/auth')
    .send({username: '[correo electrónico protegido]', passsword: 'abc123'})
    .end(function(err, res) {
        expect(err).to.be.null;
        expect(res).to.have.status(200);
    });

moca

Mocha es un marco de prueba para Node.js que le brinda la flexibilidad de ejecutar código asíncrono (o síncrono) en serie. Cualquier excepción no detectada se muestra junto con el caso de prueba en el que se lanzó, lo que facilita identificar exactamente qué falló y por qué.

Para usar Mocha, te sugiero que lo instales globalmente con npm:

1
$ npm install mocha -g

Querrá que sea una instalación global ya que el comando mocha se usa para ejecutar las pruebas del proyecto en su directorio local.

Crear casos de prueba es fácil usando el método describe(). describe() se utiliza para dar estructura a sus pruebas agrupando otras llamadas describe() y métodos it(), que es donde se encuentran las pruebas reales. Esto probablemente se describa mejor con un ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
"use strict";

var expect = require('chai').expect;

describe('Math', function() {
    describe('#abs()', function() {
        it('should return positive value of given negative number', function() {
            expect(Math.abs(-5)).to.be.equal(5);
        });
    });
});

Tenga en cuenta que con las pruebas de Mocha no necesita requerir() ninguno de los métodos de Mocha. Estos métodos se proporcionan globalmente cuando se ejecutan con el comando mocha.

Para ejecutar estas pruebas, guarde su archivo y use el comando mocha:

1
2
3
4
5
6
7
8
9
$ mocha .


  Math
    #abs()
       should return positive value of given number 


  1 passing (9ms)

El resultado es un desglose de las pruebas que se ejecutaron y sus resultados. Observe cómo las llamadas describe() anidadas se trasladan a la salida de resultados. Es útil tener todas las pruebas para un método o función dado anidados juntos.

Estos métodos son la base del marco de prueba de Mocha. Úsalos para redactar y organizar tus pruebas como quieras. Veremos un ejemplo de esto en la siguiente sección.

Exámenes de escritura {#tests de escritura}

La forma recomendada de organizar sus pruebas dentro de su proyecto es colocarlas todas en su propio directorio /test. De forma predeterminada, Mocha comprueba las pruebas unitarias utilizando los globs ./test/*.js y ./test/*.coffee. A partir de ahí, cargará y ejecutará cualquier archivo que llame al método describe().

Personalmente, me gusta usar el sufijo .test.js para los archivos fuente que realmente contienen pruebas de Mocha. Entonces, una estructura de directorio de ejemplo podría verse así:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
├── package.json
├── lib
   ├── db.js
   ├── models.js
   └── util.js
└── test
    ├── db.test.js
    ├── models.test.js
    ├── util.test.js
    └── util.js

util.js no contendría ninguna prueba de unidad real, solo funciones de utilidad para ayudar con las pruebas.

Puede usar cualquier estructura que tenga sentido para usted (que es lo bueno de Mocha), pero esto funcionó bien para mí en el pasado.

Cuando se trata de escribir las pruebas, es útil organizarlas usando los métodos describe(). Puede organizarlos por característica, función, archivo u otra cosa.

Ampliando nuestro ejemplo de la sección anterior, elegiremos organizar las pruebas por función, lo que da como resultado algo similar a esto:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
"use strict";

var expect = require('chai').expect;

describe('Math', function() {
    describe('#abs()', function() {
        it('should return positive value of given negative number', function() {
            expect(Math.abs(-5)).to.be.equal(5);
        });

        it('should return positive value of given positive number', function() {
            expect(Math.abs(3)).to.be.equal(3);
        });

        it('should return 0 given 0', function() {
            expect(Math.abs(0)).to.be.equal(0);
        });
    });
});

Ejecutar las pruebas le daría el resultado:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ mocha .


  Math
    #abs()
       should return positive value of given negative number 
       should return positive value of given positive number 
       should return 0 given 0 


  3 passing (11ms)

Ampliando aún más (lo prometo, este es el último que mostraré), incluso podría tener pruebas para múltiples métodos en un solo archivo. En este caso los métodos se agrupan por el objeto Math:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
"use strict";

var expect = require('chai').expect;

describe('Math', function() {
    describe('#abs()', function() {
        it('should return positive value of given negative number', function() {
            expect(Math.abs(-5)).to.be.equal(5);
        });

        it('should return positive value of given positive number', function() {
            expect(Math.abs(3)).to.be.equal(3);
        });

        it('should return 0 given 0', function() {
            expect(Math.abs(0)).to.be.equal(0);
        });
    });

    describe('#sqrt()', function() {
        it('should return the square root of a given positive number', function() {
            expect(Math.sqrt(25)).to.be.equal(5);
        });

        it('should return NaN for a given negative number', function() {
            expect(Math.sqrt(-9)).to.be.NaN;
        });

        it('should return 0 given 0', function() {
            expect(Math.sqrt(0)).to.be.equal(0);
        });
    });
});

Producción:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ mocha .


  Math
    #abs()
       should return positive value of given negative number 
       should return positive value of given positive number 
       should return 0 given 0 
    #sqrt()
       should return the square root of a given positive number 
       should return NaN for a given negative number 
       should return 0 given 0 


  6 passing (10ms)

Está bien, entiendes la idea.

Es cierto que la mayoría de las pruebas unitarias no son tan simples. Muchas veces probablemente necesitará otros recursos para realizar sus pruebas, como una base de datos o algún otro recurso externo. Para configurar esto, podemos usar uno o más de los siguientes métodos Gancho de moca:

  • before(): se ejecuta antes de todas las pruebas en el bloque dado
  • beforeEach(): se ejecuta antes de cada prueba en el bloque dado
  • after(): se ejecuta después de todas las pruebas en el bloque dado
  • afterEach(): se ejecuta después de cada prueba en el bloque dado

Estos ganchos son el lugar perfecto para realizar los trabajos de instalación y desmontaje necesarios para sus pruebas. Como ya mencioné, uno de los casos de uso comunes es establecer una conexión a su base de datos antes de ejecutar las pruebas, que se muestra en el siguiente ejemplo:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
"use strict";

var expect = require('chai').expect;
var Camo = require('camo');
var User = require('../models').User;

describe('Users', function() {

    var database = null;

    before(function(done) {
        Camo.connect('mongodb://localhost/app_test').then(function(db) {
            database = db;
            return database.dropDatabase();
        }).then(function() {}).then(done, done);
    });

    afterEach(function(done) {
        database.dropDatabase().then(function() {}).then(done, done);
    });

    describe('#save()', function() {
        it('should save User data to database', function(done) {
            // Use your database here...
        });
    });

    describe('#load()', function() {
        it('should load User data from database', function(done) {
            // Use your database here...
        });
    });
});

Antes de ejecutar cualquiera de las pruebas, se ejecuta la función enviada a nuestro método before() (y solo se ejecuta una vez durante las pruebas), lo que establece una conexión con la base de datos. Una vez hecho esto, se ejecutan nuestras suites de prueba.

Dado que no queremos que los datos de un conjunto de pruebas afecten a nuestras otras pruebas, debemos borrar los datos de nuestra base de datos después de ejecutar cada conjunto. Para esto es afterEach(). Usamos este enlace para borrar todos los datos de la base de datos después de ejecutar cada caso de prueba, de modo que podamos comenzar desde cero para las próximas pruebas.

Ejecutando pruebas {#ejecutando pruebas}

Para la mayoría de los casos, esta parte es bastante simple. Suponiendo que ya instaló Mocha y navegó al directorio del proyecto, la mayoría de los proyectos solo necesitan usar el comando mocha sin argumentos para ejecutar sus pruebas.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ mocha


  Math
    #abs()
       should return positive value of given negative number 
       should return positive value of given positive number 
       should return 0 given 0 
    #sqrt()
       should return the square root of a given positive number 
       should return NaN for a given negative number 
       should return 0 given 0 


  6 passing (10ms)

Esto es ligeramente diferente a nuestros ejemplos anteriores ya que no necesitábamos decirle a Mocha dónde estaban ubicadas nuestras pruebas. En este ejemplo, el código de prueba está en la ubicación esperada de /test.

Sin embargo, existen algunas opciones útiles que puede utilizar al ejecutar pruebas. Si algunas de sus pruebas fallan, por ejemplo, probablemente no desee ejecutar todo el conjunto cada vez que realice un cambio. Para algunos proyectos, el conjunto completo de pruebas puede tardar unos minutos en completarse. Eso es mucho tiempo perdido si realmente solo necesita ejecutar una prueba.

Para casos como este, debe decirle a Mocha qué pruebas ejecutar. Esto se puede hacer usando las opciones -g <patrón> o -f <subcadena>.

Nuevamente, usando los ejemplos de arriba, podemos usar la opción -g para ejecutar solo nuestras pruebas #sqrt():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ mocha -g sqrt


  Math
    #sqrt()
       should return the square root of a given positive number 
       should return NaN for a given negative number 
       should return 0 given 0 


  3 passing (10ms)

Tenga en cuenta que las pruebas #abs() no se incluyeron en esta ejecución. Si planifica de acuerdo con los nombres de sus pruebas, esta opción se puede utilizar para ejecutar solo secciones específicas de sus pruebas.

Sin embargo, estas no son las únicas opciones útiles. Aquí hay algunas opciones más para Mocha que quizás desee consultar:

  • --invert: Invierte las coincidencias -g y -f
  • --recursive: Incluir subdirectorios
  • --harmony: habilite todas las funciones de armonía en Node

Puede consultar la lista completa de opciones usando el comando mocha -h, o en esta página.

Dónde obtener más información

Hay mucho más sobre este tema de lo que podemos cubrir en una breve publicación de blog, por lo que si desea obtener más información, le recomiendo que consulte los siguientes recursos:

En lugar de solo leer la documentación, también puede probar un curso sobre este tema, al que vinculé a continuación. El instructor entra en gran detalle sobre cómo configurar Mocha, Chai y Sinon para probar el código de Node.js, así como videos detallados para cada tema.

Aprenda pruebas unitarias de Javascript con Mocha, Chai y Sinon

Es un gran complemento para este artículo, y también es más fácil de digerir que los sitios web de documentación gracias a su formato de video.

Conclusión

Tenga en cuenta que tanto Mocha como Chai se pueden usar para probar casi cualquier tipo de proyecto de Node, ya sea una biblioteca, una herramienta de línea de comandos o incluso un sitio web. Utilizando las diversas opciones y complementos disponibles para usted, debería poder satisfacer sus necesidades de prueba con bastante facilidad. Cada una de estas bibliotecas es muy útil para validar su código y debe usarse en casi todos sus proyectos de Node.

Esperemos que esto haya servido como una introducción útil a Mocha y Chai. Hay mucho más que aprender de lo que he presentado aquí, así que asegúrese de consultar los documentos para obtener más información.

¿Tiene algún consejo útil para escribir pruebas de Mocha/Chai? ¡Cuéntanos en los comentarios! anos en los comentarios!*