Как правильно проверить обещания с мокко и чай?


следующий тест ведет себя странно:

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 120

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

или с современным узлом и асинхронным / 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() звонки везде.

это преимущество Мокко имеет над другими библиотеками, как Жасмин на данный момент. Вы также можете чтобы проверить Чай, Как И Обещал что сделало бы его еще проще (нет .then), но лично я предпочитаю ясность и простоту текущая версия

как уже указал здесь, более новые версии Мокко уже обещают-осведомлены. Но так как ОП спросил конкретно о чае, то справедливо указать на chai-as-promised пакет, который обеспечивает чистый синтаксис для тестирования обещаний:

используя chai-as-promised

вот как вы можете использовать chai-as-promised для тестирования обоих 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-promised:

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 () - блок.