Test del codice Node.js con Mocha e Chai

Introduzione

La scrittura di unit test è qualcosa che sia i principianti che gli ingegneri esperti in genere rimandano alle fasi successive di sviluppo, ma sono fondamentali per uno sviluppo software stabile e robusto.

La premessa di base di sviluppo basato sui test (TDD) sta scrivendo i tuoi test anche prima di iniziare a scrivere codice. È un grande obiettivo a cui aspirare, ma ci vuole molta disciplina e pianificazione quando si cerca di seguirne i principi! Per rendere l'intero processo molto più semplice, puoi ricorrere a framework di test e asserzioni potenti e facili da usare, come Moca ed Chai.

In questo articolo inizieremo presentandoti queste due librerie e poi ti mostreremo come usarle insieme per creare rapidamente unit test leggibili e funzionali.

Chai

Chai è una libreria di asserzioni che fornisce sia il file BDD (sviluppo comportamentale) ed TDD (sviluppo basato sui test) stili di programmazione per testare il codice ed è pensato per essere abbinato a una libreria di test che ti consenta di organizzare i test. È molto comunemente abbinato alla Mocha.

Ha tre API principali:

  • should
  • expect
  • assert

var.should.equal(var2)


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


assert.equal(var1, var2)

In questo articolo ci concentreremo sullo stile BDD utilizzando Chai expect interfaccia, anche se usare altre interfacce/stili secondo la tua intuizione è perfettamente accettabile. IL assert L'interfaccia è il framework di asserzioni TDD più simile.

expect utilizza un'API dal linguaggio molto naturale per scrivere le tue asserzioni, il che renderà i tuoi test più facili da scrivere e migliorare in seguito. Questo viene fatto concatenando insieme i getter per creare ed eseguire l'asserzione, rendendo più semplice tradurre i requisiti in codice:

let user = {name: 'Scott'};


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

Nota: Vedi come puoi praticamente leggere l'affermazione in un linguaggio naturale e capire cosa sta succedendo? Questo è uno dei principali vantaggi dell'utilizzo di una libreria di asserzioni come Chai!

Alcuni altri esempi di questi getter sono:

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

Molti di questi getter possono essere concatenati insieme e utilizzati con metodi di asserzione come true, ok, existe empty per creare alcune asserzioni complesse in una sola riga:

"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: È possibile trovare un elenco completo dei metodi disponibili qui.

Potresti anche voler controllare l'elenco dei disponibili i plugin per Chai. Ciò rende molto più semplice testare funzionalità più complesse.

Fai chai-http ad esempio, che è un plugin che ti aiuta a testare i percorsi del server:

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

Organizzare casi di test con Mocha – description() e it()

Mocha è un framework di test per Node che ti offre la flessibilità di eseguire codice asincrono (o sincrono) in serie. Eventuali eccezioni non rilevate vengono visualizzate accanto al caso di test in cui sono state lanciate, facilitando l'identificazione esatta di cosa non è riuscito e perché.

Si consiglia di installare Mocha a livello globale:

$ npm install mocha -g

Avrai bisogno che sia un'installazione globale dal momento che mocha Il comando viene utilizzato per eseguire i test per il progetto nella directory locale.

Cosa fa questo pezzo di codice?

it() dovrebbe restituire X. it() definisce i casi di test e Mocha li eseguirà ciascuno it() come test unitario. Per organizzare più unit test, possiamo describe() una funzionalità comune e quindi strutturare i test Mocha.

Probabilmente è meglio descriverlo con un esempio 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);
        });
    });
});

Nel describe() metodo, abbiamo definito a nome del test, chiamato #abs(). Puoi anche eseguire test individualmente con il loro nome: questo verrà trattato in seguito.

Nota: Con i test Mocha, non è necessario require() uno qualsiasi dei metodi Mocha. Questi metodi vengono forniti a livello globale quando eseguiti con mocha comando.

Per eseguire questi test, salva il file e utilizza il file mocha comando:

$ mocha .

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


  1 passing (9ms)

L'output è una ripartizione dei test eseguiti e dei relativi risultati. Nota come nidificato describe() le chiamate vengono trasferite all'output dei risultati. È utile avere tutti i test per un determinato metodo o funzionalità annidati insieme.

Questi metodi costituiscono la base per il framework di test Mocha. Usali per comporre e organizzare i tuoi test come preferisci. Ne vedremo un esempio nella prossima sezione.

Test di scrittura con Mocha e Chai

Il modo consigliato per organizzare i test all'interno del progetto è inserirli tutti separatamente /test directory. Per impostazione predefinita, Mocha controlla gli unit test utilizzando i glob ./test/*.js ed ./test/*.coffee. Da lì, caricherà ed eseguirà qualsiasi file che chiami il file describe() metodo.

Dai un'occhiata alla nostra guida pratica e pratica per l'apprendimento di Git, con le migliori pratiche, gli standard accettati dal settore e il cheat sheet incluso. Smetti di cercare su Google i comandi Git e in realtà imparare esso!

È comune aggiungere il suffisso ai file di test .test.js per i file sorgente che contengono test Mocha:

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

util.js nel test la directory non conterrebbe alcun test unitario, solo funzioni di utilità per aiutare con i test.

Note:: Puoi utilizzare qualunque struttura abbia senso per te, i test unitari vengono selezionati automaticamente.

Quando si tratta di scrivere effettivamente i test, è utile organizzarli utilizzando il file describe() metodi. Puoi organizzarli per caratteristica, funzione, file o qualsiasi altro livello arbitrario. Ad esempio, un file di test organizzato per descrivere il funzionamento a livello di funzione appare simile a:

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

L'esecuzione dei test ti darebbe quindi l'output:

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

Espandendosi ulteriormente, potresti persino avere test per più metodi in un singolo file. In questo caso, i metodi sono raggruppati per Math oggetto:

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

Che si traduce in:

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

Mocha Hooks – prima(), dopo(), primaEach() e afterEach()

Certo, la maggior parte dei test unitari non sono così semplici. Molte volte probabilmente avrai bisogno di altre risorse per eseguire i tuoi test, come un database o qualche altra risorsa esterna (o una loro simulazione/stub). Per configurarlo, possiamo utilizzare uno o più dei seguenti Gancio per moka metodi:

  • before(): viene eseguito prima di tutti i test nel blocco specificato
  • beforeEach(): viene eseguito prima di ogni test nel blocco specificato
  • after(): viene eseguito dopo tutti i test nel blocco specificato
  • afterEach(): viene eseguito dopo ogni test nel blocco specificato

Questi ganci sono il luogo perfetto per eseguire il lavoro di installazione e smontaggio necessario per i test. Uno dei casi d'uso comuni è stabilire una connessione al database prima di eseguire i test:

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

Prima in qualsiasi dei test eseguiti, la funzione inviata al ns before() viene eseguito (e viene eseguito solo una volta durante i test), che stabilisce una connessione al database. Una volta fatto ciò, vengono eseguite le nostre suite di test.

Poiché non vorremmo che i dati di una suite di test influenzino gli altri nostri test, dobbiamo cancellare i dati dal nostro database dopo l'esecuzione di ciascuna suite. Questo è ciò afterEach() è per. Usiamo questo hook per cancellare successivamente tutti i dati del database ogni viene eseguito il test case, quindi possiamo iniziare da zero per i test successivi.

Esecuzione di test sulla moka

Per la maggior parte dei casi, questa parte è piuttosto semplice. Supponendo che tu abbia già installato Mocha e navigato nella directory del progetto, la maggior parte dei progetti deve semplicemente utilizzare il file mocha comando senza argomenti per eseguire i test:

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

Questo è leggermente diverso dai nostri esempi precedenti poiché non avevamo bisogno di dire a Mocha dove si trovavano i nostri test. In questo esempio, il codice di test si trova nella posizione prevista di /test.

Esistono, tuttavia, alcune opzioni utili che potresti voler utilizzare durante l'esecuzione dei test. Se alcuni dei tuoi test falliscono, ad esempio, probabilmente non vorrai eseguire l'intera suite ogni volta che apporti una modifica. Per alcuni progetti, il completamento dell'intera suite di test potrebbe richiedere alcuni minuti. È molto tempo sprecato se hai davvero bisogno di eseguire solo un test.

Per casi come questo, dovresti dì a Mocha quali test eseguire. Questo può essere fatto usando il -g or -f opzioni.

Per eseguire test individuali, è possibile fornire il file -g flag e aggiungi un modello comune tra i test che desideri eseguire. Ad esempio, se vuoi eseguire il file #sqrt() test:

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

Si noti che il #abs() i test non sono stati inclusi in questa esecuzione. Se pianifichi di conseguenza i nomi dei test, questa opzione può essere utilizzata per eseguire solo sezioni specifiche dei test.

Queste non sono le uniche opzioni utili, tuttavia. Ecco alcune altre opzioni per Mocha che potresti voler controllare:

  • --invert: Inverte -g ed -f fiammiferi
  • --recursive: include sottodirectory
  • --harmony: abilita tutte le funzionalità di armonia in Node

Note:: Puoi controllare l'elenco completo delle opzioni utilizzando il file mocha -h comando o su questa pagina.

Dove saperne di più? C'è molto di più su questo argomento di quanto possiamo trattare in un breve articolo, quindi se vuoi saperne di più ti consigliamo di consultare il sito ufficiale Moca ed Chai documentazione.

Conclusione

Tieni presente che sia Mocha che Chai possono essere utilizzati per testare praticamente qualsiasi tipo di progetto Node, che si tratti di una libreria, di uno strumento da riga di comando o persino di un sito Web. Utilizzando le varie opzioni e plugin a tua disposizione, dovresti essere in grado di soddisfare le tue esigenze di test abbastanza facilmente. Ognuna di queste librerie è molto utile per convalidare il tuo codice e dovrebbe essere utilizzata in quasi tutti i tuoi progetti Node.

Si spera che questo sia servito come utile introduzione a Mocha e Chai. C'è molto di più da imparare rispetto a quello che ho presentato qui, quindi assicurati di controllare la documentazione per maggiori informazioni.

Timestamp:

Di più da Impilamento