Probando el código de Node.js con Mocha y Chai

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

    Desarrollo impulsado por pruebas, lo que significa que escribe sus pruebas antes que su código, es un gran objetivo por el que luchar, pero requiere disciplina y planificación cuando está programando. Para que todo este proceso sea mucho más fácil, necesita marcos de prueba y afirmación potentes y fáciles de usar, que es exactamente lo que Moca y Chai son.

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

    Chai

    Chai es una biblioteca de aserciones que proporciona tanto BDD y 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 Chai expect interfaz.

    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 en el futuro. Esto se hace encadenando getters para crear y ejecutar la aserción, lo que facilita la traducción de los requisitos al código:

    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:

    • 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. Algunos ejemplos:

    "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);
    

    Se puede encontrar una lista completa de los métodos disponibles aquí.

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

    Tomar chai-http por ejemplo, que es un plugin que le ayuda a probar las rutas del servidor.

    "use strict";
    
    var chai = require('chai');
    var chaiHttp = require('chai-http');
    
    chai.use(chaiHttp);
    
    chai.request(app)
        .put('/api/auth')
        .send({username: '[email protected]', 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 asincrónico (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 la identificación exacta de qué falló y por qué.

    Para usar Mocha, le sugiero que lo instale globalmente con npm:

    $ npm install mocha -g
    

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

    La creación de casos de prueba es fácil con el describe() método. describe() se utiliza para estructurar sus pruebas agrupando otros describe() llamadas y it() métodos juntos, que es donde se encuentran las pruebas reales. Esto probablemente se describe mejor con un ejemplo:

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

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

    Pruebas de escritura

    La forma recomendada de organizar sus pruebas dentro de su proyecto es ponerlas todas en su propio /test directorio. 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 describe() método.

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

    ├── 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 unitaria real, solo funciones de utilidad para ayudar con las pruebas.

    Puedes usar cualquier estructura que tenga sentido para ti (que es lo bueno de Mocha), pero esto me ha funcionado bien en el pasado.

    Cuando se trata de redactar las pruebas, es útil organizarlas utilizando el describe() métodos. 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 que se ve así:

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

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

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

    Salida:

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

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

    "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, 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 nuestras suites de prueba.

    Dado que no queremos que los datos de un conjunto de pruebas afecten a nuestras otras pruebas, necesitamos borrar los datos de nuestra base de datos después de ejecutar cada conjunto. Esto es lo que afterEach() es para. Usamos este enlace para borrar todos los datos de la base de datos después de que se ejecuta cada caso de prueba, de modo que podamos comenzar desde una pizarra limpia para las próximas pruebas.

    Ejecución de pruebas

    Para la mayoría de los casos, esta parte es bastante simple. Suponiendo que ya haya instalado Mocha y haya navegado al 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 puede utilizar al realizar pruebas. Si algunas de sus pruebas fallan, por ejemplo, probablemente no desee ejecutar toda la suite cada vez que realice un cambio. Para algunos proyectos, el conjunto de pruebas completo 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 debe ejecutar. Esto se puede hacer usando el -g <pattern> o -f <sub-string> opciones.

    Nuevamente, usando los ejemplos de arriba, podemos usar el -g opción de ejecutar solo nuestro #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)
    

    Note 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 para Mocha que es posible que desee consultar:

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

    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 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 simplemente leer la documentación, también puede probar un curso sobre este tema, al que vinculé a continuación. El instructor detalla cómo configurar el uso de Mocha, Chai y Sinon para probar el código Node.js, así como videos detallados para cada tema.

    Aprenda las pruebas unitarias de JavaScript con Mocha, Chai y Sinon

    Es un gran plugin 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 plugins 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.

    ¿Tiene algún consejo útil para escribir pruebas de Mocha / Chai? ¡Háznoslo saber en los comentarios!

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *