Come faccio a verificare correttamente le promesse con moka e chai?

Il seguente test si comporta in modo strano:

it('Should return the exchange rates for btc_ltc', function(done) { var pair = 'btc_ltc'; shapeshift.getRate(pair) .then(function(data){ expect(data.pair).to.equal(pair); expect(data.rate).to.have.length(400); done(); }) .catch(function(err){ //this should really be `.catch` for a failed request, but //instead it looks like chai is picking this up when a test fails done(err); }) }); 

Come dovrei gestire correttamente una promise respinta (e testarla)?

Come dovrei gestire correttamente un test fallito (es: expect(data.rate).to.have.length(400);

Ecco l’implementazione che sto testando:

 var requestp = require('request-promise'); var shapeshift = module.exports = {}; var url = 'http://shapeshift.io'; shapeshift.getRate = function(pair){ return requestp({ url: url + '/rate/' + pair, json: true }); }; 

La cosa più semplice da fare sarebbe utilizzare il supporto di promesse incorporato che Mocha ha nelle versioni recenti:

 it('Should return the exchange rates for btc_ltc', function() { // no done var pair = 'btc_ltc'; // note the return return shapeshift.getRate(pair).then(function(data){ expect(data.pair).to.equal(pair); expect(data.rate).to.have.length(400); });// no catch, it'll figure it out since the promise is rejected }); 

O con il nodo moderno e asincrono / attendi:

 it('Should return the exchange rates for btc_ltc', async () => { // no done const pair = 'btc_ltc'; const data = await shapeshift.getRate(pair); expect(data.pair).to.equal(pair); expect(data.rate).to.have.length(400); }); 

Dato che questo approccio è promettente, è più facile da testare e non dovrai pensare agli strani casi a cui stai pensando come le chiamate dispari done() .

Questo è un vantaggio che Mocha ha su altre librerie come Jasmine al momento. Potresti anche voler controllare Chai As Promised che lo renderebbe ancora più semplice (no .then ) Ma personalmente preferisco la chiarezza e la semplicità della versione attuale

Come già sottolineato qui , le versioni più recenti di Mocha sono già a conoscenza di Promise. Ma dal momento che l’OP ha chiesto specificatamente a Chai, è giusto sottolineare il pacchetto chai-as-promised che fornisce una syntax pulita per testare le promesse:

usando chai-come-promesso

Ecco come puoi usare chai-come-promesso per testare sia i casi di resolve che quelli di reject per una Promessa:

 var chai = require('chai'); var expect = chai.expect; var chaiAsPromised = require("chai-as-promised"); chai.use(chaiAsPromised); ... it('resolves as promised', function() { return expect(Promise.resolve('woof')).to.eventually.equal('woof'); }); it('rejects as promised', function() { return expect(Promise.reject('caw')).to.be.rejectedWith('caw'); }); 

senza chai-come-promesso

Per rendere davvero chiaro su cosa viene provato, ecco lo stesso esempio codificato senza chai-come-promesso:

 it('resolves as promised', function() { return Promise.resolve("woof") .then(function(m) { expect(m).to.equal('woof'); }) .catch(function(m) { throw new Error('was not supposed to fail'); }) ; }); it('rejects as promised', function() { return Promise.reject("caw") .then(function(m) { throw new Error('was not supposed to succeed'); }) .catch(function(m) { expect(m).to.equal('caw'); }) ; }); 

Ecco la mia opinione:

  • usando async/await
  • non ha bisogno di moduli chai extra
  • evitando il problema delle catture, @TheCrazyProgrammer ha sottolineato sopra

Una funzione di promise ritardata, che non riesce, se ha un ritardo di 0:

 const timeoutPromise = (time) => { return new Promise((resolve, reject) => { if (time === 0) reject({ 'message': 'invalid time 0' }) setTimeout(() => resolve('done', time)) }) } // ↓ ↓ ↓ it('promise selftest', async () => { // positive test let r = await timeoutPromise(500) assert.equal(r, 'done') // negative test try { await timeoutPromise(0) // a failing assert here is a bad idea, since it would lead into the catch clause… } catch (err) { // optional, check for specific error (or error.type, error. message to contain …) assert.deepEqual(err, { 'message': 'invalid time 0' }) return // this is important } assert.isOk(false, 'timeOut must throw') log('last') }) 

Il test positivo è piuttosto semplice. L’errore imprevisto (simulato da 500→0 ) non supererà automaticamente il test, poiché la promise respinta si intensifica.

Il test negativo usa l’idea del try-catch. Tuttavia: “lamentarsi” di un passaggio indesiderato avviene solo dopo la clausola catch (in questo modo non finisce nella clausola catch (), innescando ulteriori errori fuorvianti.

Affinché questa strategia funzioni, è necessario restituire il test dalla clausola catch. Se non vuoi testare nient’altro, usa un altro it () – block.