首页 文章

如何用mocha和chai正确测试承诺?

提问于
浏览
131

以下测试表现得很奇怪:

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 回答

  • 35

    最简单的方法是使用Mocha在最近版本中内置的promises支持:

    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 ),但我个人更喜欢当前版本的清晰度和简洁性

  • 211

    正如已经指出here,新版本的Mocha已经是Promise-aware . 但是由于OP特别询问了Chai,所以只能指出 chai-as-promised 包为测试承诺提供了一个干净的语法:

    使用chai-as-promised

    以下是如何使用chai-as-promise来测试Promise的 resolvereject 个案:

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

    这是我的看法:

    • 使用 async/await

    • 不需要额外的柴模块

    • 避免了捕获问题,@ TheCrazyProgrammer在上面指出

    A delayed promise function, that fails, if given a delay of 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')
    })
    

    Positive test 很简单 . 意外故障(由 500→0 模拟)将自动失败,因为被拒绝的承诺会升级 .

    Negative test 使用try-catch-idea . 但是:'complaining'关于不期望的传递仅在catch子句之后发生(这样,它不会在catch()子句中结束,从而触发进一步但误导性的错误 .

    要使此策略起作用,必须从catch子句返回测试 . 如果你不想测试其他任何东西,请使用另一个() - 阻止 .

相关问题