我有一个调用另一个异步函数g的异步函数f。为了测试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);
});
});但是,即使我在f中调用g时忘记使用await,这个测试也会通过。
捕获此错误的方法之一是让存根返回一个虚拟的promise,并检查是否调用了它的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();
});但这有点麻烦。有什么方便的方法可以做到这一点吗?
发布于 2018-11-29 07:52:19
您的f示例显示了有缺陷的代码设计,如果您在没有async/await语法的情况下编写相同的函数,则会变得更加明显:
f(n) { return g(n).then(()=>{}); }
这实现了同样的行为-- g的解析是否变得难以判断(假设你不知道f是否返回了g的promise,这就相当于不知道f是否等待了g)。如果f对g的结果不感兴趣,它应该只返回它,而不是隐藏它。然后你就可以简单地测试结果了。
如果您的观点是,f可能必须依次触发几个async调用await几个g_1,g_2,...要解决这个问题,您可以通过在g_n+1的存根中断言g_n的虚拟承诺已经解决来构建测试链。一般来说,测试虚拟承诺的状态的方法是很好的。
发布于 2018-12-04 00:03:25
与其截断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解析的时间一样长。如下所示:
async f(n) {
this.g(n);
await new Promise((resolve) => {
setImmediate(() => {
resolve();
});
});
}这将导致我编写的测试通过,即使它仍然是不正确的。因此,这实际上取决于您对测试的严格程度。您是否希望从字面上看不可能出现假阳性?或者,如果一些明显的骗局可能会把它抛到脑后,这是可以的吗?
在大多数情况下,我发现后者是可以的,但实际上这取决于您和/或您的团队。
https://stackoverflow.com/questions/53529435
复制相似问题