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
相关产品推荐
相关产品推荐

