¿Cómo pruebo correctamente las promesas con mocha y chai?

La siguiente prueba se comporta de manera extraña:

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

¿Cómo debo manejar adecuadamente una promesa rechazada (y probarla)?

¿Cómo debo manejar adecuadamente una prueba fallida (es decir: expect(data.rate).to.have.length(400);

Aquí está la implementación que estoy probando:

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

    Lo más fácil sería usar las promesas integradas de soporte que Mocha tiene en las versiones recientes:

     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 Nodo moderno y asincrónico / espera:

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

    Dado que este enfoque es prometedor de principio a fin, es más fácil de probar y no tendrá que pensar en los casos extraños en los que está pensando, como las llamadas impares done() todas partes.

    Esta es una ventaja que Mocha tiene sobre otras bibliotecas como Jasmine en este momento. También es posible que desee comprobar Chai As Promised, que lo haría aún más fácil (no .then ), pero personalmente prefiero la claridad y la simplicidad de la versión actual

    Como ya se señaló aquí , las versiones más nuevas de Mocha ya son conscientes de Promise. Pero dado que el OP preguntó específicamente sobre Chai, es justo señalar el paquete chai-as-promised que proporciona una syntax limpia para probar las promesas:

    usando chai-como-prometido

    A continuación, le mostramos cómo puede usar chai-as-prometido para probar casos de resolve y reject de una promesa:

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

    sin chai-como-prometido

    Para que quede muy claro qué se está probando, aquí está el mismo ejemplo codificado sin chai-as-prometido:

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

    Aquí está mi opinión:

    • usando async/await
    • no necesita módulos de chai adicionales
    • evitando el problema de la captura, @TheCrazyProgrammer señaló anteriormente

    Una función de promesa retrasada, que falla, si se le da un retraso de 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') }) 

    La prueba positiva es bastante simple. Fallo inesperado (simule por 500→0 ) fallará la prueba automáticamente, a medida que la promesa rechazada se intensifique.

    La prueba negativa usa la try-catch-idea. Sin embargo, “quejarse” de un pase no deseado ocurre solo después de la cláusula catch (de esa manera, no termina en la cláusula catch (), lo que desencadena errores adicionales pero engañosos.

    Para que esta estrategia funcione, uno debe devolver la prueba de la cláusula catch. Si no quieres probar nada más, usa otro bloque it ().