Comment tester correctement les promesses avec le moka et le chai?

Le test suivant se comporte bizarrement:

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

Comment dois-je gérer correctement une promesse rejetée (et la tester)?

Comment gérer correctement un test ayant échoué (ex: expect(data.rate).to.have.length(400);

Voici l’implémentation que je teste:

 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 chose la plus simple à faire serait d’utiliser le support de promesses intégré que Mocha a dans les versions récentes:

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

    Ou avec les nœuds et asynchrones / attendus modernes:

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

    Comme cette approche est une promesse de bout en bout, c’est plus facile à tester et vous n’aurez pas à penser aux cas étranges auxquels vous pensez, comme les appels impairs done() partout.

    C’est un avantage que Mocha a sur d’autres bibliothèques comme Jasmine en ce moment. Vous voudrez peut-être aussi vérifier Chai As Promised qui le rendrait encore plus facile (no .then ) mais personnellement, je préfère la clarté et la simplicité de la version actuelle

    Comme nous l’avons déjà souligné, les nouvelles versions de Mocha sont déjà compatibles avec Promise. Mais comme l’OP demandait spécifiquement à propos de Chai, il est juste de souligner le paquet chai-as-promised qui fournit une syntaxe propre pour tester les promesses:

    en utilisant chai comme promis

    Voici comment vous pouvez utiliser chai-as-promise pour tester les cas de resolve et de reject pour une promesse:

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

    sans chai comme promis

    Pour bien comprendre ce qui est testé, voici le même exemple codé sans chai-as-promise:

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

    Voici ma prise:

    • en utilisant async/await
    • ne nécessitant pas de modules chai supplémentaires
    • en évitant le problème de capture, @TheCrazyProgrammer souligné ci-dessus

    Une fonction de promesse retardée, qui échoue si on lui atsortingbue un délai 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') }) 

    Le test positif est plutôt simple. Un échec inattendu (simuler par 500→0 ) échouera automatiquement au test, car la promesse rejetée dégénère.

    Le test négatif utilise l’idée try-catch. Cependant, «se plaindre» d’une passe non désirée ne se produit qu’après la clause catch (de cette manière, elle ne se retrouve pas dans la clause catch (), ce qui déclenche des erreurs supplémentaires mais trompeuses.

    Pour que cette stratégie fonctionne, il faut retourner le test à partir de la clause catch. Si vous ne voulez pas tester autre chose, utilisez un autre bloc it ().