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

如何用Mocha+Chai测试未处理Promise拒绝及包装函数异常处理

我来帮你搞定这个Mocha+Chai的测试场景,核心要验证两点:一是trackerFactory对返回拒绝Promise的watchFunction的处理逻辑,二是确保没有未处理的Promise拒绝漏出来。下面给你两种常用的测试写法,附带详细解释:

写法一:使用原生done回调+事件监听

这种写法不需要额外插件,适合原生Mocha+Chai环境:

it("handles a rejection by the watched function", (done) => {
  // 1. 模拟一个返回拒绝Promise的watchFunction
  const intentionalError = new Error("Watched function rejected");
  const watchFunction = () => Promise.reject(intentionalError);

  // 2. 监听未处理的Promise拒绝,用来检测是否有漏网的拒绝
  let unhandledRejectionDetected = false;
  const rejectionHandler = (err) => {
    unhandledRejectionDetected = true;
    console.warn("Unhandled rejection caught:", err.message);
  };
  process.on('unhandledRejection', rejectionHandler);

  // 3. 创建tracker实例并触发调用
  const tracker = trackerFactory({ watchFunction });
  
  tracker.call()
    .catch((err) => {
      // 4. 验证拒绝的错误是否符合预期(如果tracker会向外传递拒绝的话)
      expect(err).to.equal(intentionalError);
    })
    .finally(() => {
      // 5. 必须移除事件监听,避免干扰后续测试
      process.off('unhandledRejection', rejectionHandler);
      // 6. 断言没有未处理的拒绝发生
      expect(unhandledRejectionDetected).to.be.false;
      done();
    });
});

关键步骤解释:

  • 步骤1:模拟一个明确返回拒绝Promise的watchFunction,用自定义错误方便后续断言
  • 步骤2:监听Node.js的unhandledRejection事件——如果trackerFactory的包装逻辑没有正确捕获watchFunction的Promise拒绝,这个事件就会触发
  • 步骤4:如果你的tracker.call()设计是将watchFunction的拒绝向外抛出,这里就捕获并验证错误;如果tracker内部会吞掉拒绝(比如做了错误处理),则不需要这个catch块,直接断言tracker.call()是fulfilled状态
  • 步骤5&6:移除监听是测试的关键(否则会污染其他用例),最后断言没有未处理的拒绝,确保包装逻辑正确处理了Promise的拒绝

写法二:使用async/await+chai-as-promised插件

如果你的项目已经用了chai-as-promised,写法会更简洁:
首先确保安装插件:

npm install chai-as-promised --save-dev

然后在测试文件中引入并使用:

const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
const expect = chai.expect;

// ...

it("handles a rejection by the watched function", async () => {
  const intentionalError = new Error("Watched function rejected");
  const watchFunction = () => Promise.reject(intentionalError);
  const tracker = trackerFactory({ watchFunction });

  // 验证tracker.call()返回的Promise是否按预期拒绝
  await expect(tracker.call()).to.be.rejectedWith(intentionalError);

  // 额外验证没有未处理的拒绝
  let unhandledRejection = null;
  const handler = (err) => { unhandledRejection = err; };
  process.on('unhandledRejection', handler);

  // 再次调用一次,确认没有未处理的拒绝漏出
  await tracker.call().catch(() => {});
  
  process.off('unhandledRejection', handler);
  expect(unhandledRejection).to.be.null;
});

优势:

  • 用async/await替代回调链,代码更易读
  • chai-as-promised提供的rejectedWith断言可以直接验证Promise的拒绝状态和错误信息

内容的提问来源于stack exchange,提问作者Tom Klino

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 07:08:56