利用 Mocha 进行 BDD 风格测试 中介绍了 Mocha 测试框架和 Chai 断言库的使用。JavaScript 天生就是异步的, 这意味着在 JavaScript 测试中往往会需要异步断言。 本文介绍如何使用 Chaichai-as-promised 来测试 Promise。

Mocha 测试异步代码

Mocha 本身是支持异步测试的。只需要为 describe 回调函数添加一个 done 参数, 成功时调用 done(),失败时调用 done(err)。例如:

var expect = require('chai').expect;
describe('db', function() {
    it('#get', function(done) {
        db.get('foo', function(err, foo){
            if(err) done(err);        
            expect(foo).to.equal('bar');
            done();
        });
    });
});
  • 如果未调用 done 函数,Mocha 会一直等待直到超时。
  • 如果未添加 done 参数,Mocha 会直接返回成功,不会捕获到异步的断言失败。例如:
it('#get', function(){
    setTimeout(function(){
        expect(1).to.equal(2);
    }, 100);
});

运行上述测试 Mocha 总会提示 Passing。

Mocha 怎么知道是否要等待异步断言呢?因为 JavaScript 中的 Function 有一个 length 属性, 通过它可以获得该函数的形参个数。Mocha 通过传入回调的 length 来判断是否需要等待。

Mocha 测试 Promise

测试 Promise 有两种方式:可以采用上述 done 的方式,也可以直接返回该 Promise。 注意 Promise 也属于异步代码,如果未采用上述方式,Mocha 将无法捕捉到异步的断言失败。

done 回调的方式

describe('#find()', function() {
    it('respond with matching records', function(done) {
        db.find({ name: 'harttle' })
            .then(function(user){
                expect(user.name).to.equal('harttle');
                done();
            })
            .catch(err => done(err));
    });
});

直接返回 Promise 的方式

describe('#find()', function() {
    it('respond with matching records', function() {
        return db.find({ name: 'harttle' }).then(function(user){
            expect(user.name).to.equal('harttle');
        });
    });
});

expect 失败或 db.find 失败都会导致 Promise 失败(Rejected), 这时 Mocha 判定测试失败,否则判定测试成功。

注意两种方式不可同时使用,即返回了 Promise 就不要调用 done,否则 Mocha 会报错。

Chai As Promised

chai-as-promised 是 Chai 的一个断言库插件, 该插件可以大大简化 Promise 相关的断言。 它为 Promise 提供了 .should.eventrually 属性, 通过该属性可以使用任何 Chai 提供的 BDD 断言方法。

先使用 NPM 安装 chai-as-promised

npm install --save-dev chai-as-promised

现在我们用 chai as promised 重写上述测试逻辑:

var chai = require('chai');
chai.use(require("chai-as-promised"));

describe('#find()', function() {
    it('respond with matching records', function() {
        return expect(db.find({ name: 'harttle' }))
            .to.eventually.have.property('name', 'harttle');
    });
    it('should be fulfilled', function(){
        return expect(db.remove('user')).to.eventually.be.fulfilled;
    });
});

参考阅读

本文采用 知识共享署名 4.0 国际许可协议(CC-BY 4.0)进行许可,转载注明来源即可: https://harttle.land/2016/07/12/async-test-with-chai-as-promised.html。如有疏漏、谬误、侵权请通过评论或 邮件 指出。