Come posso build la mia suite di test in modo asincrono?

Sto provando a creare test moka per i miei controller usando una configurazione che deve essere caricata asincrona. Di seguito è il mio codice. Tuttavia, quando viene eseguito il test della moka, non esegue alcun test, visualizzando 0 passing . I console.log non vengono mai nemmeno chiamati. Ho provato a fare before(next => config.build().then(next)) all’interno della descrizione, ma anche se i test sono eseguiti, before non viene mai chiamato. C’è un modo per caricare la configurazione una volta prima di eseguire qualsiasi test?

 'use strict'; const common = require('./common'); const config = require('../config'); config .build() .then(test); function test() { console.log(1); describe('Unit Testing', () => { console.log(2); require('./auth'); }); } 

Dovresti eseguire Mocha con l’opzione --delay , e quindi usare run() una volta che hai finito di creare la tua suite di test. Ecco un esempio derivato dal codice che mostri nella domanda:

 'use strict'; function test() { console.log(1); describe('Unit Testing', () => { console.log(2); it("test", () => { console.log(3); }); }); // You must use --delay for `run()` to be available to you. run(); } setTimeout(test, 1000); 

Sto usando setTimeout per simulare un’operazione asincrona. L’utilizzo di --delay e run() consente di creare una suite risultante da un calcolo asincrono. Nota, tuttavia, che la suite deve essere costruita in un colpo solo. (Non si può avere un processo asincrono all’interno di describe che effettuerà chiamate ad it . Questo non funzionerà.)


Una cosa che non dovresti assolutamente fare è quello che suggerisce rob3c: chiamare describe (o entrambe) da un hook. Questo è un errore che ogni tanto le persone fanno, quindi vale la pena di affrontarlo nei dettagli. Il problema è che non è supportato da Mocha, e quindi non esiste una semantica stabilita associata alla chiamata describe o dall’interno di un hook. Oh, è ansible scrivere semplici esempi che funzionano come ci si potrebbe aspettare, ma:

  1. Quando la suite diventa più complessa, il comportamento della suite non corrisponde più a nulla di sensato.

  2. Poiché non vi sono semantiche associate a questo approccio, le nuove versioni di Mocha possono gestire l’utilizzo errato in modo diverso e interrompere la suite.

Considera questo semplice esempio:

 const assert = require("assert"); const p = Promise.resolve(["foo", "bar", "baz"]); describe("top", () => { let flag; before(() => { flag = true; return p.then((names) => { describe("embedded", () => { for (const name of names) { it(name, () => { assert(flag); }); } }); }); }); after(() => { flag = false; }); it("regular test", () => { assert(flag); }); }); 

Quando lo eseguiamo, otteniamo:

  top ✓ regular test embedded 1) foo 2) bar 3) baz 1 passing (32ms) 3 failing // [stack traces omitted for brevity] 

Cosa sta succedendo qui? Non dovrebbero passare tutti i test? Impostiamo flag su true nel hook before per la descrizione top . Tutti i test che creiamo dovrebbero vedere flag come true , no? L’indizio è nell’output sopra: quando creiamo test all’interno di un hook, Mocha metterà i test da qualche parte ma potrebbe non trovarsi in una posizione che rifletta la struttura dei blocchi describe nel codice. Quello che succede in questo caso è che Mocha aggiunge semplicemente i test creati nell’uncinetto alla fine della suite, al di fuori della descrizione top , quindi l’ after hook viene eseguito prima dei test creati dynamicmente, e otteniamo un risultato contro-intuitivo.

Usando --delay e run() , possiamo scrivere una suite che si comporta in modo concordante con l’intuizione:

 const assert = require("assert"); const p = Promise.resolve(["foo", "bar", "baz"]).then((names) => { describe("top", () => { let flag; before(() => { flag = true; }); after(() => { flag = false; }); describe("embedded", () => { for (const name of names) { it(name, () => { assert(flag); }); } }); it("regular test", () => { assert(flag); }); }); run(); }); 

Produzione:

  top ✓ regular test embedded ✓ foo ✓ bar ✓ baz 4 passing (19ms) 

Si potrebbe semplicemente separare la logica del testing e del caricamento interamente, avvolgere il loader in una promise che blocca il test fino all’esecuzione della configurazione (molto semplice con async / attendi se stai usando node8, altrimenti solo Promise.each i risultati).

Se davvero non vuoi farlo, promuovi il tuo framework di test, che ti permetterebbe di trattare tutti i blocchi descrittivi come codice asincrono.

Il problema con l’uso della funzione --delay flag line e run() callback che @Louis ha menzionato nella sua risposta accettata è che run() è un unico hook globale che ritarda la suite di test root . Pertanto, devi costruirli tutti in una volta (come ha accennato), il che può rendere i test di organizzazione una seccatura (per non dire altro).

Tuttavia, preferisco evitare le bandiere magiche ogni volta che sia ansible, e certamente non voglio dover gestire la mia intera suite di test con un singolo callback globale run() . Fortunatamente, c’è un modo per creare dynamicmente i test su una base per file, e non richiede alcun flag speciale 🙂

Per creare dynamicmente i test It() in qualsiasi file di origine di test utilizzando dati ottenuti in modo asincrono, è ansible (ab) utilizzare l’hook before() con un test It() segnaposto per garantire che moka attende fino a before() viene eseguito before() . Ecco l’esempio dalla mia risposta a una domanda correlata , per comodità:

 before(function () { console.log('Let the abuse begin...'); return promiseFn(). then(function (testSuite) { describe('here are some dynamic It() tests', function () { testSuite.specs.forEach(function (spec) { it(spec.description, function () { var actualResult = runMyTest(spec); assert.equal(actualResult, spec.expectedResult); }); }); }); }); }); it('This is a required placeholder to allow before() to work', function () { console.log('Mocha should not require this hack IMHO'); });