Как правильно проверить обещания с мокко и чай?
Следующий тест ведет себя странно:
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);
})
});
Как правильно обработать отклоненное обещание (и проверить его)?
Как правильно обрабатывать проваленный тест (например: expect(data.rate).to.have.length(400);
?
Вот реализация, которую я тестирую:
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
});
};
3 ответа
Проще всего было бы использовать встроенную поддержку обещаний, которую Mocha имеет в последних версиях:
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
});
Или с современным Node и async/await:
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);
});
Так как этот подход - обещания от начала до конца, его легче протестировать, и вам не придется думать о странных случаях, о которых вы думаете, как о нечетных done()
звонки везде.
Это преимущество Mocha перед другими библиотеками, такими как Jasmine на данный момент. Вы также можете проверить Chai As Promised, что сделает его еще проще (нет .then
) но лично я предпочитаю четкость и простоту текущей версии
Как уже указывалось здесь, более новые версии Mocha уже поддерживают Promise. Но так как ФП спросил конкретно о Чай, было бы справедливо указать на chai-as-promised
пакет, который обеспечивает чистый синтаксис для тестирования обещаний:
используя чай как обещано
Вот как вы можете использовать chai-as-обещано, чтобы проверить оба resolve
а также reject
случаи для обещания:
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');
});
без чая, как и обещал
Чтобы прояснить, что тестируется, вот тот же пример, кодированный без chai-as-обещано:
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'); })
;
});
Вот мое мнение:
- с помощью
async/await
- не требуются дополнительные модули Chai
- @TheCrazyProgrammer указывал выше, чтобы избежать проблемы с уловом
Функция отложенного обещания, которая не выполняется, если задана задержка 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')
})
Положительный тест довольно прост. Неожиданный сбой (смоделировать 500→0
) автоматически провалит тест, поскольку отклоненное обещание возрастает.
Отрицательный тест использует try-catch-idea. Однако "жалоба" на нежелательный проход происходит только после предложения catch (таким образом, он не заканчивается в предложении catch(), вызывая дальнейшие, но вводящие в заблуждение ошибки.
Чтобы эта стратегия работала, необходимо вернуть тест из предложения catch. Если вы не хотите ничего тестировать, используйте другой блок it().
Tehre - лучшее решение. Просто верните ошибку с done в блоке 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
});
});
этот тест не пройдёт со следующим сообщением: AssertionError: expected 1 to equal 11