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

Introducción

Escribir pruebas unitarias es algo que tanto los principiantes como los ingenieros experimentados suelen posponer para fases posteriores del desarrollo; sin embargo, son clave para un desarrollo de software estable y sólido.

La premisa básica de desarrollo dirigido por pruebas (TDD) está escribiendo sus pruebas incluso antes de comenzar a codificar. Es un gran objetivo por el que esforzarse, ¡pero se necesita mucha disciplina y planificación cuando intentas seguir sus principios! Para facilitar mucho todo este proceso, puede recurrir a marcos de prueba y aserción potentes y fáciles de usar, como Mocha y Chai.

En este artículo, comenzaremos presentándole estas dos bibliotecas y luego le mostraremos cómo usarlas juntas para crear rápidamente pruebas unitarias legibles y funcionales.

Chai

Chai es una biblioteca de aserciones que proporciona tanto la BDD (desarrollo impulsado por el comportamiento) y TDD (desarrollo basado en pruebas) estilos de programación para probar código y está diseñado para combinarse con una biblioteca de pruebas que le permite organizar las pruebas. Se combina muy comúnmente con Mocha.

Tiene tres API principales:

  • should
  • expect
  • assert

var.should.equal(var2)


expect.var.to.be.equal(var2)


assert.equal(var1, var2)

A lo largo de este artículo, nos centraremos en el estilo BDD utilizando Chai. expect interfaz, aunque usar otras interfaces/estilos según su propia intuición está perfectamente bien. El assert La interfaz es el marco de aserción TDD común más parecido.

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

let user = {name: 'Scott'};


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

Nota: ¿Ves cómo puedes leer la afirmación en un lenguaje natural y entender lo que está pasando? ¡Esa es una de las principales ventajas de utilizar una biblioteca de aserciones como Chai!

Algunos ejemplos más de estos captadores son:

  • to
  • be
  • is
  • and
  • has
  • have

Muchos de estos captadores se pueden encadenar y utilizar con métodos de aserción como true, ok, existy empty para crear algunas afirmaciones complejas en una sola línea:

"use strict";

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


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


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


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

Nota: Puede encontrar una lista completa de los métodos disponibles. esta página.

Quizás también quieras consultar la lista de disponibles plugins para Chai. Esto hace que sea mucho más fácil probar funciones más complejas.

¡Prepárate! chai-http por ejemplo, que es un complemento que te ayuda a probar las rutas del servidor:

"use strict";

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

chai.use(chaiHttp);

chai.request(app)
    .put('/api/auth')
    .send({username: '[email protected]', password: 'abc123'})
    .end(function(err, res) {
        expect(err).to.be.null;
        expect(res).to.have.status(200);
    });

Organización de casos de prueba con Mocha: describe() y it()

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

Se recomienda instalar Mocha globalmente:

$ npm install mocha -g

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

¿Qué hace este fragmento de código?

it() debería devolver X. it() define casos de prueba, y Mocha ejecutará cada uno it() como prueba unitaria. Para organizar múltiples pruebas unitarias, podemos describe() una funcionalidad común y, por lo tanto, estructurar las pruebas de Mocha.

Probablemente esto se describa mejor con un ejemplo concreto:

"use strict";
const 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);
        });
    });
});

En describe() método, definimos un nombre de la prueba, lo cual se conoce como #abs(). También puede ejecutar pruebas individualmente por su nombre; esto se tratará más adelante.

Nota: Con las pruebas de Mocha, no es necesario require() cualquiera de los métodos Mocha. Estos métodos se proporcionan globalmente cuando se ejecutan con el mocha mando.

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

$ 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 el anidado describe() las llamadas se transfieren a la salida de resultados. Es útil tener todas las pruebas de un método o característica determinada anidadas juntas.

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.

Pruebas de redacción con Mocha y Chai

La forma recomendada de organizar sus pruebas dentro de su proyecto es ponerlas todas en su propio /test directorio. De forma predeterminada, Mocha busca pruebas unitarias utilizando los globos. ./test/*.js y ./test/*.coffee. Desde allí, cargará y ejecutará cualquier archivo que llame al describe() método.

Consulte nuestra guía práctica y práctica para aprender Git, con las mejores prácticas, los estándares aceptados por la industria y la hoja de trucos incluida. Deja de buscar en Google los comandos de Git y, de hecho, aprenden ella!

Es común agregar el sufijo a los archivos de prueba con .test.js para los archivos fuente que contienen pruebas de Mocha:

├── package.json
├── lib
│   ├── db.js
│   ├── models.js
│   └── util.js
└── test
    ├── db.test.js
    ├── models.test.js
    ├── util.test.js
    └── util.js

util.js existentes test El directorio no contendría ninguna prueba unitaria, solo funciones de utilidad para ayudar con las pruebas.

Note: Puede utilizar cualquier estructura que tenga sentido para usted; las pruebas unitarias se seleccionan automáticamente.

Cuando se trata de escribir las pruebas, ayuda organizarlas utilizando el describe() métodos. Puede organizarlos por característica, función, archivo o cualquier otro nivel arbitrario. Por ejemplo, un archivo de prueba organizado para describir el funcionamiento a nivel de función tiene el siguiente aspecto:

"use strict";

const 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:

$ 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, es posible que incluso tenga pruebas para varios métodos en un solo archivo. En este caso, los métodos se agrupan por Math :

"use strict";

const 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);
        });
    });
});

Lo que resulta en:

$ 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)

Ganchos Mocha: antes (), después (), antes de cada () y después de cada ()

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

  • 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. Uno de los casos de uso comunes es establecer una conexión a su base de datos antes de ejecutar las pruebas:

"use strict";

const expect = require('chai').expect;
let User = require('../models').User;

describe('Users', function() {

    let database = null;

    before(function(done) {
        
    });

    afterEach(function(done) {
        
    });

    describe('#save()', function() {
        it('should save User data to database', function(done) {
            
        });
    });

    describe('#load()', function() {
        it('should load User data from database', function(done) {
            
        });
    });
});

Antes cualquier de las pruebas se ejecutan, la función enviada a nuestro before() Se ejecuta el método (y solo se ejecuta una vez durante las pruebas), lo que establece una conexión a la base de datos. Una vez hecho esto, se ejecutan nuestros conjuntos de pruebas.

Como 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. Esto es lo que afterEach() es para. Usamos este gancho para borrar todos los datos de la base de datos después cada una Se ejecuta el caso de prueba, por lo que podemos comenzar desde cero para las próximas pruebas.

Ejecución de pruebas de Mocha

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

$ 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 quizás desee utilizar al ejecutar pruebas. Si algunas de sus pruebas fallan, por ejemplo, probablemente no desee ejecutar todo el paquete cada vez que realice un cambio. Para algunos proyectos, el conjunto de pruebas completo puede tardar unos minutos en completarse. Es una gran pérdida de tiempo si realmente sólo necesita realizar una prueba.

Para casos como este, debes decirle a Mocha qué pruebas ejecutar. Esto se puede hacer usando el -g or -f .

Para ejecutar pruebas individuales, puede suministrar el -g marque y agregue un patrón común entre las pruebas que desea ejecutar. Por ejemplo, si desea ejecutar el #sqrt() pruebas:

$ 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)

Observe que el #abs() Las pruebas no se incluyeron en esta ejecución. Si planifica en consecuencia 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 de Mocha que quizás quieras consultar:

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

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

¿Dónde aprender más? Hay mucho más sobre este tema de lo que podemos cubrir en un breve artículo, por lo que si desea obtener más información, le recomendamos que consulte el artículo oficial. Mocha y Chai documentación.

Conclusión

Tenga en cuenta que tanto Mocha como Chai se pueden utilizar 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.

Con suerte, esto ha 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.

Sello de tiempo:

Mas de Abuso de pila