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

Jest测试Express应用时MongoDB及定时任务存在开放句柄无法退出求解

问题解决办法

1. 处理定时任务的Timeout开放句柄

你导入../main的时候,全局定时任务会直接初始化运行,且不会自动销毁,导致Timeout句柄残留,两种修复方案可选:

  • 测试环境禁用定时任务:在定时任务初始化代码外层加环境判断
// main.js 对应的定时任务代码部分
if (process.env.NODE_ENV !== 'test') {
  // 15:00 hours
  schedule.scheduleJob({ hour: 13, minute: 0 }, async () => {
    await hoursCa(15);
  });
}
  • 显式销毁定时任务:将所有定时任务实例存储后对外暴露销毁方法
// main.js
export const scheduleJobs = []
// 存任务实例
const job1 = schedule.scheduleJob({ hour: 13, minute: 0 }, async () => {
  await hoursCa(15);
});
scheduleJobs.push(job1)
// 暴露销毁方法
export const clearAllScheduleJobs = () => {
  scheduleJobs.forEach(job => job.cancel())
}

测试的afterAll中调用clearAllScheduleJobs()即可。

2. 处理mongoose连接的TLSWRAP开放句柄

错误显示你创建了两个mongoose连接:mongoose.connect()的默认连接、mongoose.createConnection()生成的自定义连接,当前仅关闭了默认连接,自定义连接未关闭导致句柄残留:

  • 首先修改../config/db文件,将createConnection返回的connection实例导出
// config/db.js 对应部分
let connection;
if (db) {
  connection = mongoose.createConnection(db);
  autoIncrement.initialize(connection);
}
export { connection } // 新增导出

3. 修正测试文件的资源关闭逻辑

当前afterAll中server.close()是异步回调方法,没有等待执行完成就执行后续操作,导致端口句柄残留,需要将其包装为Promise等待执行完成,同时补充所有资源的关闭逻辑:

// 测试文件新增导入
import { clearAllScheduleJobs } from '../main';
import { connection as customConnection } from '../config/db';

afterAll(async () => {
  console.log('closing');
  // 先销毁定时任务
  clearAllScheduleJobs()
  // 等待http服务完全关闭
  await new Promise((resolve, reject) => {
    server.close(err => err ? reject(err) : resolve(null))
  })
  // 关闭所有mongoose连接
  await mongoose.disconnect();
  await customConnection.close();
  await disconnect();
  // 3s延迟非必须,仅当存在未覆盖的异步逻辑时可保留
});

可选补充配置

如果还有残余句柄,可以在jest配置文件(jest.config.js)中添加:

module.exports = {
  // 其他原有配置
  detectOpenHandles: true,
  forceExit: true, // 极端场景下强制杀死残留进程
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 01:45:02