如何验证回调函数未被调用且通过测试用例?
测试回调未被调用的解决方案
问题背景
我有一个循环检查条件直到满足才执行回调的函数:
let loopWithSleep = (callback) => { if (conds.every(cond => { return cond; })) { console.log("wooho"); callback(); } else { console.log("waiting..."); setTimeout(loopWithSleep, 500, callback); } };
编写测试用例验证回调不会被调用时,遇到了超时错误:
describe('Callback', () => { it('should NOT use the provided callback', (done) => { var conds = [true, false]; loopWithSleep(done); }); });
报错信息:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
尝试用chai.assert.throws()包裹测试也没解决问题。
核心问题分析
- 变量作用域错误:测试里定义的
conds是局部变量,但loopWithSleep引用的是全局的conds,测试的条件设置根本没生效。 - 测试框架机制限制:Mocha这类测试框架会等待
done()被调用才结束测试,如果回调永远不会执行,就会触发超时。
解决方案
步骤1:修正函数的条件依赖
先把conds改成函数参数,让测试可以控制条件:
// 修改后的loopWithSleep,支持传入conds参数 let loopWithSleep = (conds, callback) => { if (conds.every(cond => cond)) { console.log("wooho"); callback(); } else { console.log("waiting..."); setTimeout(() => loopWithSleep(conds, callback), 500); } };
步骤2:用Spy断言回调未被调用
使用Sinon.js的spy(或Chai Spy)来跟踪回调是否被调用,同时手动控制测试结束时机,避免超时:
const sinon = require('sinon'); const { expect } = require('chai'); describe('Callback', () => { it('should NOT call the provided callback when conditions are not met', (done) => { // 创建一个spy代替done,用于跟踪调用情况 const callbackSpy = sinon.spy(); const conds = [true, false]; // 启动循环检查 loopWithSleep(conds, callbackSpy); // 设置一个比框架默认超时短的时间(比如1500ms),确保循环执行几次后再断言 setTimeout(() => { // 断言spy从未被调用 expect(callbackSpy.called).to.be.false; // 手动结束测试 done(); }, 1500); // 清理定时器,避免影响后续测试 after(() => { clearTimeout(); }); }); // 设置测试超时时间,避免框架默认超时提前触发 this.timeout(3000); });
替代方案:不使用Spy,手动验证
如果不想用Sinon,也可以用一个标记变量来跟踪回调是否被调用:
describe('Callback', () => { it('should NOT call the provided callback when conditions are not met', (done) => { let callbackCalled = false; const conds = [true, false]; loopWithSleep(conds, () => { callbackCalled = true; }); setTimeout(() => { // 断言回调未被触发 expect(callbackCalled).to.be.false; done(); }, 1500); }); this.timeout(3000); });
关键注意事项
- 一定要清理定时器,否则会导致后续测试出现异常或内存泄漏。
- 设置测试超时时间要比你等待的断言时间长,避免框架提前终止测试。
- 不要依赖全局变量传递测试条件,尽量把依赖项作为参数传入函数,提高可测试性。
内容的提问来源于stack exchange,提问作者zajer
相关产品推荐
相关产品推荐

