You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何验证回调函数未被调用且通过测试用例?

测试回调未被调用的解决方案

问题背景

我有一个循环检查条件直到满足才执行回调的函数:

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()包裹测试也没解决问题。

核心问题分析

  1. 变量作用域错误:测试里定义的conds是局部变量,但loopWithSleep引用的是全局的conds,测试的条件设置根本没生效。
  2. 测试框架机制限制: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 13:15:27