Le test suivant se comporte étrangement:
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 refusée (et la tester)?
Comment gérer correctement un test qui a échoué (c.-à-d.: 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 les promesses intégrées au support de Mocha 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 Node moderne et asynchrone/wait:
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, il est plus facile de la tester et vous n’aurez pas à penser aux cas étranges auxquels vous pensez, comme aux appels done()
impairs, partout.
C'est un avantage que Mocha a par rapport aux autres bibliothèques comme Jasmine pour le moment. Vous voudrez peut-être aussi vérifier Chai Comme Promis , ce qui le rendrait encore plus facile (pas de .then
), mais personnellement, je préfère la clarté et la simplicité de la version actuelle.
Comme déjà indiqué ici , les nouvelles versions de Mocha sont déjà compatibles avec Promise. Mais depuis que l'OP a spécifiquement posé des questions sur Chai, il est juste de signaler le paquetage chai-as-promised
qui fournit une syntaxe propre pour tester les promesses:
Voici comment utiliser chai-as-promise pour tester les deux cas resolve
et reject
d'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');
});
Pour bien comprendre ce qui va être testé, voici le même exemple codé sans chai comme promis:
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'); })
;
});
Tehre est une meilleure solution. Il suffit de renvoyer l'erreur with done dans un bloc catch.
// ...
it('fail', (done) => {
// any async call that will return a Promise
ajaxJson({})
.then((req) => {
expect(1).to.equal(11); //this will throw a error
done(); //this will resove the test if there is no error
}).catch((e) => {
done(e); //this will catch the thrown error
});
});
ce test échouera avec le message suivant: AssertionError: expected 1 to equal 11
async/await
Fonction de promesse retardée, qui échoue si on lui donne un retard 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')
})
test positif est assez simple. Un échec inattendu (simulé par 500→0
) échouera automatiquement au test, à mesure que la promesse rejetée dégénère.
test négatif utilise le principe try-catch-idea. Cependant: "se plaindre" à propos d'une passe indésirable ne se produit qu'après la clause catch (ainsi, cela ne se retrouvera pas dans la clause catch (), ce qui déclenchera des erreurs supplémentaires mais trompeuses.
Pour que cette stratégie fonctionne, il faut renvoyer le test à partir de la clause catch. Si vous ne voulez rien tester d'autre, utilisez un autre bloc it () - block.