我有一个异步函数f
,它调用了另一个异步函数g
。为了测试f
是否调用g
,我正在使用sinon对g
进行存根,并断言它是使用should.js调用的。
'use strict';
require('should-sinon');
const sinon = require('sinon');
class X {
async f(n) {
await this.g(n);
// this.g(n); // I forget to insert `await`!
}
async g(n) {
// Do something asynchronously
}
}
describe('f', () => {
it('should call g', async () => {
const x = new X();
sinon.stub(x, 'g').resolves();
await x.f(10);
x.g.should.be.calledWith(10);
});
});
但是,即使我在await
中调用g
时忘记使用f
,此测试仍通过。
捕获此错误的方法之一是使存根返回伪承诺,并检查其then
是否被调用。
it('should call g', async () => {
const x = new X();
const dummyPromise = {
then: sinon.stub().yields()
};
sinon.stub(x, 'g').returns(dummyPromise);
await x.f(10);
x.g.should.be.calledWith(10);
dummyPromise.then.should.be.called();
});
但这有点麻烦。有什么方便的方法吗?
答案 0 :(得分:0)
您为f
设计的示例显示了有缺陷的代码设计,如果您使用async/await
语法编写相同的函数,则代码设计将变得更加明显:
f(n) {
return g(n).then(()=>{});
}
这实现了相同的行为-是否解决g
变得很难说(假设您不知道f
是否返回了g
的承诺,这与不知道是一样的f
是否等待g
)。如果f
对g
的结果不感兴趣,则应仅返回它,而不是将其隐藏。然后,您可以简单地测试结果。
如果您的意思是f
可能必须依次触发多个async
调用await
,并调用多个g_1
,g_2
...来解决,那么您可以通过在g_n+1
的存根中断言g_n
的伪承诺已经建立来构建测试链。通常,测试虚拟承诺的状态的方法很好。
答案 1 :(得分:0)
最好不要对then
进行存根处理,而要在下一事件循环迭代中设置一些布尔值。然后,您可以在调用g
后检查此布尔值,以确保f
等待它:
f
编辑:当然,这不是完美保证,因为您可以让it('should call g', async () => {
const x = new X();
let gFinished = false;
sinon.stub(x, 'g').callsFake(() => {
return new Promise((resolve) => {
setImmediate(() => {
gFinished = true;
resolve();
});
});
});
await x.f(10);
x.g.should.be.calledWith(10);
gFinished.should.be.true();
});
等待任何承诺,等待的时间至少要长于f
解决。像这样:
g
这将导致我编写的测试通过,即使它仍然不正确。因此,实际上,这取决于您对测试的严格程度。您是否希望误报从字面上看是不可能的?还是可以通过一些明显的诡计将其抛弃?
在大多数情况下,我发现后者是可以的,但实际上这取决于您和/或您的团队。