在 promise 链的末尾测试回调调用
Test callback invocation at the end of promise chain
我正在处理混合节点式回调和 Bluebird 承诺的代码,我需要为其编写一些单元测试。
特别是,cache.js
公开了 init()
函数,它与 promises 一起工作。然后它被另一个文件(例如 index.js
)中的 doSomething()
函数调用,该文件又接受必须在 init()
.
结束时调用的回调
伪代码如下:
// [ cache.js ]
function init() {
return performInitialisation()
.then((result) => return result);
}
// [ index.js ]
var cache = require('./cache');
function doSomething(callback) {
console.log('Enter');
cache.init()
.then(() => {
console.log('Invoking callback');
callback(null);
})
.catch((err) => {
console.log('Invoking callback with error');
callback(err);
});
console.log('Exit');
}
可能的单元测试可能是(仅显示相关代码):
// [ index.test.js ]
...
var mockCache = sinon.mock(cache);
...
it('calls the callback on success', function(done) {
mockCache.expects('init')
.resolves({});
var callback = sinon.spy();
doSomething(callback);
expect(callback).to.have.been.calledOnce;
done();
});
这个测试通过了,但是将期望更改为 not.have.been.calledOnce
也通过了,这是错误的。
此外,控制台日志顺序不正确:
Enter
Exit
Invoking callback
我研究了几种可能性,none 可行:
按照承诺使用 chai,例如expect(callback).to.eventually.have.been.calledOnce;
重构doSomething()
为简单:
function doSomething(回调){
cache.init()
.asCallback(回调);
}
任何人都可以帮助我了解我做错了什么以及如何解决它吗?
console logs are out of sequence
日志的顺序正确,因为您的 Promise
将是异步的,这意味着,至少,内部控制台日志调用 then
和 catch
将 运行 在下一个报价单上。
至于为什么测试失败是几个问题的结果,第一个是你似乎没有正确配置 sinon-chai
,或者充其量你的 calledOnce
断言是还没开始。只是为了确认,测试文件的顶部应该是这样的:
const chai = require("chai");
const sinonChai = require("sinon-chai");
chai.use(sinonChai);
如果你有它,但它仍然不能正常工作,那么可能值得在 sinon-chai
库上打开一个问题,但是,一个简单的解决方法是切换到 sinon assertions,例如
sinon.assert.calledOnce(callback)
其次,当您最终解决此问题时,您可能会发现测试现在会失败……每次都失败。原因是您在测试中遇到了与日志记录相同的问题——您在内部承诺有机会解决之前的断言。解决此问题的最简单方法实际上是使用 Mocha 中的 done
处理程序作为断言
mockCache.expects('init').resolves({});
doSomething(() => done());
换句话说,如果 done
被调用,那么您知道回调已被调用:)
根据 James' 的评论,我像这样重新访问了我的测试:
it('calls the callback on success', function(done) {
mockCache.expects('init')
.resolves({});
doSomething(done);
});
it('calls the callback on error', function(done) {
mockCache.expects('init')
.rejects('Error');
doSomething((err) => {
if (err === 'Error') {
done();
} else {
done(err);
}
});
});
我正在处理混合节点式回调和 Bluebird 承诺的代码,我需要为其编写一些单元测试。
特别是,cache.js
公开了 init()
函数,它与 promises 一起工作。然后它被另一个文件(例如 index.js
)中的 doSomething()
函数调用,该文件又接受必须在 init()
.
伪代码如下:
// [ cache.js ]
function init() {
return performInitialisation()
.then((result) => return result);
}
// [ index.js ]
var cache = require('./cache');
function doSomething(callback) {
console.log('Enter');
cache.init()
.then(() => {
console.log('Invoking callback');
callback(null);
})
.catch((err) => {
console.log('Invoking callback with error');
callback(err);
});
console.log('Exit');
}
可能的单元测试可能是(仅显示相关代码):
// [ index.test.js ]
...
var mockCache = sinon.mock(cache);
...
it('calls the callback on success', function(done) {
mockCache.expects('init')
.resolves({});
var callback = sinon.spy();
doSomething(callback);
expect(callback).to.have.been.calledOnce;
done();
});
这个测试通过了,但是将期望更改为 not.have.been.calledOnce
也通过了,这是错误的。
此外,控制台日志顺序不正确:
Enter
Exit
Invoking callback
我研究了几种可能性,none 可行:
按照承诺使用 chai,例如
expect(callback).to.eventually.have.been.calledOnce;
重构
doSomething()
为简单:function doSomething(回调){ cache.init() .asCallback(回调); }
任何人都可以帮助我了解我做错了什么以及如何解决它吗?
console logs are out of sequence
日志的顺序正确,因为您的 Promise
将是异步的,这意味着,至少,内部控制台日志调用 then
和 catch
将 运行 在下一个报价单上。
至于为什么测试失败是几个问题的结果,第一个是你似乎没有正确配置 sinon-chai
,或者充其量你的 calledOnce
断言是还没开始。只是为了确认,测试文件的顶部应该是这样的:
const chai = require("chai");
const sinonChai = require("sinon-chai");
chai.use(sinonChai);
如果你有它,但它仍然不能正常工作,那么可能值得在 sinon-chai
库上打开一个问题,但是,一个简单的解决方法是切换到 sinon assertions,例如
sinon.assert.calledOnce(callback)
其次,当您最终解决此问题时,您可能会发现测试现在会失败……每次都失败。原因是您在测试中遇到了与日志记录相同的问题——您在内部承诺有机会解决之前的断言。解决此问题的最简单方法实际上是使用 Mocha 中的 done
处理程序作为断言
mockCache.expects('init').resolves({});
doSomething(() => done());
换句话说,如果 done
被调用,那么您知道回调已被调用:)
根据 James' 的评论,我像这样重新访问了我的测试:
it('calls the callback on success', function(done) {
mockCache.expects('init')
.resolves({});
doSomething(done);
});
it('calls the callback on error', function(done) {
mockCache.expects('init')
.rejects('Error');
doSomething((err) => {
if (err === 'Error') {
done();
} else {
done(err);
}
});
});