使用 SinonJs 和 Mocha 测试包含 Promise 代码块的 Restify 路由处理程序

Testing Restify Route Handler that contains Promise Code Block, using SinonJs and Mocha

我在下面有一个 restify 操作代码块:

function retriveAll(req, res, next) {
        db.user
        .find({where: {id: 1})
        .then(function(user){
            res.send(user);
        })
        .catch(function(details){
            res.send(details.message);
        })
        .finally(function(){
            next();
        });
    }

我想测试这个动作,专门验证 res.send() 是在这个代码块 中调用的。稍后验证 res.send() 返回的数据。我正在使用 SinonJsMocha 来测试框架。这是上述方法的示例测试代码块。

describe('retrieveAll()', function() {

    reqStub = {};
    resStub = {send: sinon.stub()};
    nextStub = sinon.stub();

    beforeEach(function() {
        module.retrieveAll(reqStub, resStub, nextStub);
    });

    // this doesn't work
    // and the sub.calledCount is 0
    // i wonder if it's because the res.send() is inside a Promise code block???
    // if I move the res.send() out from Promise, just before next(), then it works
    it('should call res.send()', function() {
        sinon.assert.calledOnce(resStub.send);
    });

    // this one works
    it('should call next', function() {
        sinon.assert.calledOnce(nextStub);
    });
});

有人能解释一下吗?

beforeEach() 的回调函数接收一个 done 参数,可以调用该参数来表示异步完成。由于您的 retriveAll 函数调用最后一个参数 (next) 作为最后一个操作,您可以将该参数作为 next 值传递,它应该可以工作:

beforeEach(function(done) {
    module.retrieveAll(reqStub, resStub, done);
});

但是你会松开 nextStub,所以...或者,你可以 spy 那个 done 函数:

describe('retrieveAll()', function() {

    var reqStub = {};
    var resStub = {send: sinon.stub()};
    var nextSpy;

    beforeEach(function(done) {
        nextSpy = sinon.spy(done);
        module.retrieveAll(reqStub, resStub, done);
    });

    // this doesn't work
    // and the sub.calledCount is 0
    // i wonder if it's because the res.send() is inside a Promise code block???
    // if I move the res.send() out from Promise, just before next(), then it works
    it('should call res.send()', function() {
        sinon.assert.calledOnce(resStub.send);
    });

    // this one works
    it('should call next', function() {
        sinon.assert.calledOnce(nextSpy);
    });
});

所以,我得到了这个工作感谢 @Amit 为我指出 beforeEach

上的 done 回调

首先,我修改了retrieveAll所以next回调包含在承诺链中。我把它放在 finally 处理程序中,确保 next 将在所有过程之后被调用。

第二个,我将done传递给beforeEach,然后nextStub将监视done回调。

Third,我没有将 done cb 传递给 module.v1.retrieveAll,而是使用了 nextStub。这解决了测试 nextStub.calledOnce.

的问题

更新后的代码现在看起来是:

function retriveAll(req, res, next) {
        db.user
        .find({where: {id: 1})
        .then(function(user){
            res.send(user);
        })
        .catch(function(details){
            res.send(details.message);
        })
        .finally(function(){
            next();
        });
    }
describe('retrieveAll()', function() {

    var reqStub = {};
    var resStub = {send: sinon.stub()};
    var nextStub;

    beforeEach(function(done) {
        nextStub = sinon.spy(done);
        module.retrieveAll(reqStub, resStub, nextStub);
    });

    // this doesn't work
    // and the sub.calledCount is 0
    // i wonder if it's because the res.send() is inside a Promise code block???
    // if I move the res.send() out from Promise, just before next(), then it works
    it('should call res.send()', function() {
        sinon.assert.calledOnce(resStub.send);
    });

    // this one works
    it('should call next', function() {
        sinon.assert.calledOnce(nextStub);
    });
});

我将选择@Amit 的答案作为最佳答案,因为他帮助了我并为我提供了有关更改的线索。