如何在Mocha中使用Sinon测试多回调异步函数?
异步回调+Sinon间谍测试的最佳实践
你的测试逻辑能正常运行,但确实有更规范、更简洁的实现方式——手动维护回调计数器不仅容易出错,还浪费了Sinon和测试框架本身提供的异步处理能力。下面是几个针对这类场景的最佳实践:
直接利用函数返回的Promise,抛弃手动计数器
你的函数f本身就返回Promise,完全不需要手动写done和cbCount来跟踪回调完成状态!可以直接返回这个Promise,在then块里用Sinon的断言方法验证间谍调用:it('test', () => { const cb1Spy = sinon.spy(); const cb2Spy = sinon.spy(); // 返回Promise让测试框架自动处理异步 return f(cb1Spy, cb2Spy).then(() => { // Sinon提供的断言方法能给出清晰的错误提示 sinon.assert.called(cb1Spy); sinon.assert.called(cb2Spy); // 也可以更精确地断言调用次数 // sinon.assert.calledOnce(cb1Spy); }); });用async/await让代码更易读
现代测试框架(比如Mocha、Jest)都支持async/await语法,用它来处理异步测试会让逻辑更直观,完全避免回调嵌套:it('test', async () => { const cb1Spy = sinon.spy(); const cb2Spy = sinon.spy(); // 等待Promise完成 await f(cb1Spy, cb2Spy); // 直接断言即可 sinon.assert.calledOnce(cb1Spy); sinon.assert.calledOnce(cb2Spy); });注意你原测试里的隐藏问题
顺便提一句:你原代码里的() => { cb1Spy; checkIfDone(); }其实没有调用间谍,只是引用了变量而已!正确的做法应该是直接把间谍传进f,或者在包装函数里调用cb1Spy(),不然你的间谍永远不会被标记为已调用。用上面的Promise/async方案就不会出现这种低级错误,因为我们直接把间谍作为回调传递给函数了。如果函数不返回Promise的兼容方案
要是遇到某些特殊场景,异步函数没有返回Promise,也别手动维护计数器。可以用Sinon的断言结合轮询或者监听间谍调用状态:it('test', (done) => { const cb1Spy = sinon.spy(); const cb2Spy = sinon.spy(); f(cb1Spy, cb2Spy); // 轮询检查两个间谍是否都被调用 const verify = () => { if (cb1Spy.called && cb2Spy.called) { sinon.assert.calledOnce(cb1Spy); sinon.assert.calledOnce(cb2Spy); done(); } else { setTimeout(verify, 10); } }; verify(); });不过这种情况还是建议尽量让异步函数返回Promise,测试起来会顺畅很多。
内容的提问来源于stack exchange,提问作者Edo
相关产品推荐
相关产品推荐

