Come creare un'applicazione Node.js con Docker, Parte 4: Testing

Nella quarta parte di questa serie ci concentreremo su come creare facilmente un ambiente di testing completo per la nostra applicazione Node.js utilizzando Docker, Mocha e Chai.

Negli articoli precedenti abbiamo creato un'applicazione web in Node.js ed abbiamo imparato come gestire le fasi di deploy, sviluppo e debugging con Docker. Sebbene nelle fasi iniziali dello sviluppo il testing manuale può sembrare sufficiente per assicurarsi il corretto funzionamento della nostra app, al crescere del suo codebase e della sua complessità questa soluzione diventerà inefficiente ed avremo bisogno di scrivere dei test che verifichino in maniera automatica, veloce e capillare il comportamento di ogni sua componente.

Creare un ambiente di testing è molto semplice e non richiede particolari configurazioni per quanto riguarda il container Docker. Invece lato applicazione, possiamo usare lo stack che preferiamo. In questa guida utilizzeremo Mocha come test framework e Chai come libreria per le asserzioni.

Prerequisiti

  • I file Dockerfile, index.js e package.json del precedente articolo.

Installare Mocha e Chai

Prima di buildare una nuova immagine, dobbiamo aggiornare il file package.json e aggiungere alle dipendenze mocha, chai e chai-http (un plugin di Chai per testare gli endpoint HTTP). Possiamo farlo in diversi modi:

  • Aggiungere le dipendenze al file manualmente.
  • Avviare un container, aggiornare il file all'interno del container e propagare le modifiche all'esterno tramite un volume (abbiamo visto come fare nel secondo articolo di questa serie).
  • Avviare un container, aggiornare il file all'interno del container e copiarlo all'esterno nella nostra area di lavoro.

Dato che la prima opzione non richiede ulteriori spiegazioni e la seconda è già stata approfondita, sfruttiamo questa occasione per vedere un nuovo approccio.

Se non l'abbiamo già fatto, creiamo un'immagine dal Dockerfile che abbiamo

$ docker build --tag my-app .

Ora avviamo un container da questa immagine e modifichiamo l'entrypoint per avviare una shell al suo interno (abbiamo approfondito questa procedura nell'articolo sul debugging).

$ docker run -it --entrypoint /bin/sh my-app

Installiamo quindi le dipendenze necessarie con NPM

# npm install -D mocha chai chai-http

Completata l'installazione, chiudiamo la shell (puoi farlo usando CTRL + D o il comando exit) e copiamo/ sovrascriviamo il file package.json all'interno del nostro container nella nostra area di lavoro

$ docker cp <CONTAINER_ID>:app/package.json ./package.json

Scrivere i test

Il test che andremo a scrivere verificherà che l'applicazione mostri correttamente il messaggio di benvenuto nell'homepage.

Prima però è necessario fare una modifica al file index.js

var express = require('express');
var app = express();
var port = process.env.PORT || 3000;

app.get('/', function (req, res) {
  res.send('Hello World!');
});

if (process.env.NODE_ENV === 'test') {
  module.exports = app;
} else {
  app.listen(port);
}

Abbiamo aggiunto un blocco if in cui controlliamo, tramite la variabile d'ambiente NODE_ENV, se ci troviamo in un contesto di testing e in caso affermativo esportiamo l'oggetto app (a breve vedremo perchè) altrimenti avviamo l'applicazione normalmente.

Passiamo al test. Dato che Mocha esegue di default tutti i test presenti nella cartella test, creiamo nella nostra area di lavoro una file test/homepage.js come il seguente

// Importa le dipendenze
var chai = require('chai');
var chaiHttp = require('chai-http');
// Importa l'applicazione da testare
var app = require('../index');
// Configura Chai
chai.use(chaiHttp);
chai.should();

describe('Homepage', () => {

    it('dovrebbe mostrare il messaggio Hello World', (done) => {
        chai
            .request(app)
            .get('/')
            .end((error, response) => {
                response.text.should.equal('Hello World!');
                done();
            });
    });

});

Vediamo più nel dettaglio come è strutturato il test:

  • Usiamo chai.use() per aggiungere il plugin chai-http a Chai.
  • Usiamo chai.should() per scegliere should come stile per le asserzioni. Gli altri stili sono assert ed expect.
  • Usiamo it() per creare un test, describe() per raggrupparli in suite.
  • Con request() configuriamo un'applicazione Express di cui vogliamo testare gli endpoint usando Chai.
  • In Mocha, usiamo done() alla fine del test per gestire correttamente le asserzioni in funzioni asincrone come end().

Eseguire i test

Aggiungiamo al file package.json un script test per lanciare Mocha (nota che qui passiamo la variabile d'ambiente NODE_ENV usata in index.js)

{
  "name": "nodejs-app",
  "dependencies": {
    "express": "^4.17.0"
  },
  "scripts": {
    "start": "node index.js",
    "dev": "npx nodemon index.js",
    "debug": "node --inspect=0.0.0.0:9229 index.js",
    "test": "NODE_ENV=test npx mocha"
  },
  "devDependencies": {
    "chai": "^4.2.0",
    "chai-http": "^4.3.0",
    "mocha": "^6.1.4",
    "nodemon": "^1.19.1"
  }
}

Buildiamo una nuova immagine della nostra app ed avviamo un container

$ docker build --tag my-app .
$ docker run -d -p 4000:3000 my-app

A questo punto possiamo eseguire i test in due modi:

  • eseguendoli come un processo all'esterno del container (si sceglie questa opzione quando, ad esempio, si intende lanciarli in una pipeline CI/CD)

    $ docker exec <CONTAINER_ID> npm test
  • eseguendoli come un processo all'interno del container avviando una shell in esso

    $ docker exec -it <CONTAINER_ID> /bin/sh
    # npm test

Conclusioni

Giunti a questo punto abbiamo imparato come gestire facilmente con Docker le varie fasi dello sviluppo di un'applicazione Node.js. Nel prossimo articolo parleremo dell'integrazione con altri servizi e vedremo come aggiungere un database alla nostra app usando un nuovo tool, Docker Compose.

Come creare un'applicazione Node.js con Docker, Parte 5: Servizi

Come creare un'applicazione Node.js con Docker, Parte 5: Servizi

Nella quinta parte di queste serie impareremo come utilizzare Docker Compose per gestire un'applicazione containerizzata ed aggiungervi dei servizi.

Come creare un'applicazione Node.js con Docker, Parte 1: Deploy

Come creare un'applicazione Node.js con Docker, Parte 1: Deploy

In questo articolo vedremo come utilizzare Docker per eseguire un'applicazione Node.js senza più temere lunghe e difficoltose configurazioni.

Come creare un'applicazione Node.js con Docker, Parte 2: Sviluppo

Come creare un'applicazione Node.js con Docker, Parte 2: Sviluppo

Docker è un ottimo strumento non solo per il deploy delle applicazioni ma anche per tutte le altri fasi del ciclo di vita di un software. In questa seconda parte vedremo il suo utilizzo nello sviluppo della nostra applicazione web Node.js.