Come strutturare il mio codice app.js per rendere più facile la verifica

1

Ho un paio di luci Philip Hue nel corridoio. Questi mostrano lo stato di costruzione sulle loro luci con una piccola applicazione Node.JS. Funziona come un fascino.

Attualmente sono in procinto di testare e refactoring di questa applicazione, ma nel mio app.js ho alcune righe di codice in cui ho difficoltà a capire come testarle automaticamente.

if(config.enabledHueUpdateJobs) {
    var Hue = require('./hue.js');
    var Lights = require('./lights.js');

    var hallwayLights = new Lights(new Hue());
    hallwayLights.scheduleOn('45 7 * * 1-5');
    hallwayLights.scheduleOff('30 17 * * 1-5');    
    hallwayLights.scheduleUpdate('*/30 * 7-18 * * 1-5', buildServer.getFailedBuildCount);
}

Ho una copertura del 100% per Hue.js e Lights.js 'classi', ma il codice di implementazione effettivo non è coperto. Ho avuto un problema in cui ho modificato i metodi pubblici delle "classi", ma non il codice di implementazione, ma questo non è stato rilevato dai miei test.

Ho pensato di spostare questo codice in una "classe" con una funzione .start() , ma comunque avrei ancora una o due righe non testate.

Per favore, suggerisci come posso ristrutturare il mio codice (o alcune best practice / pattern che dovrei ricercare / utilizzare) per rendere questo testabile migliore.

Aggiorna :

  • Voglio che il nostro build-server distribuisca una versione funzionante dopo che tutti i miei test sono stati eseguiti. Quindi non devo controllare manualmente il sistema.
  • Mi piacerebbe avere un flusso di lavoro TDD, vorrei rimanere nel mio IDE e avere fiducia nelle mie ultime modifiche.
  • Preferibilmente non voglio avviare 'nodo app.js' e testarlo con qualche sistema di testing blackbox
posta Niels van Reijmersdal 17.11.2016 - 18:18
fonte

1 risposta

0

Ho trovato una soluzione funzionante per scrivere un test di integrazione per il mio codice. Proverò a spiegare la mia implementazione.

Ho aggiunto un modo per avviare e interrompere il mio codice in app.js. (Stavo già usando una strategia simile per i miei test di selenio per un altro progetto) In questo modo posso avviare e interrompere il codice app.js dai test, senza avviare 'node app.js' dalla riga di comando.

codice app.js:

exports.start = function () {
    buildServer.updateCaches();

    var hallwayLights = new Lights(new Hue(config.hueServerUrl, config.hueUserName));
    jobs.push(hallwayLights.scheduleOn(config.lightsOnSchedule));
    jobs.push(hallwayLights.scheduleOff(config.lightsOffSchedule));    
    jobs.push(hallwayLights.scheduleUpdate(config.lightsUpdateSchedule, buildServer.getFailedBuildCount)); 
}

exports.close = function() {
    jobs.forEach(function (job) {
        job.cancel();
    }); 
}

if (!isStartedFromTests()) { exports.start(); }
function isStartedFromTests() { return module.id != require.main.id; }

Nei test (utilizzando ava ) I nock tutte le chiamate API esterne previste e verifica che siano state chiamate.

codice test.integration.js:

test.cb('Radiator: Should execute light schedules When the app is started', t => {
    // Arrange
    t.plan(3);
    var bsNocks = new buildServerNocks(config.buildserverServerName, config.buildserverPort);
    bsNocks.buildSuccess('Project1_Build1');
    bsNocks.buildSuccess('Project1_Build2');
    bsNocks.buildFailed('Project2_SubProject_Build3');

    var light1 = nock(config.hueServerUrl + config.hueUserName).put('/lights/1/state', hue.colors['red']).reply(200);
    var light2 = nock(config.hueServerUrl + config.hueUserName).put('/lights/2/state', hue.colors['green']).reply(200);
    var light3 = nock(config.hueServerUrl + config.hueUserName).put('/lights/3/state', hue.colors['green']).reply(200);

    // Act
    app.start();

    // Assert
    setTimeout(function() {
        t.is(light1.done())
        t.is(light2.done())
        t.is(light3.done())
        app.close();        
        t.end();
    }, 1200);
});

Nel file di configurazione development.js ho impostato la cron-schedule su ogni secondo, quindi è molto veloce. Ho spostato le configurazioni effettive in production.js. Questa configurazione è descritta in questo post del blog .

lightsUpdateSchedule: '*/1 * * * * *'

Ho verificato che funziona come previsto cambiando alcuni nomi di funzioni interne come scheduleOn a jobOn e verifica che il test non abbia esito positivo.

Sebbene funzioni, non sono ancora convinto che questa sia la soluzione migliore e più leggibile. Ancora aperto per altre risposte o idee.

    
risposta data 18.11.2016 - 11:30
fonte