如何解决Jest测试中mongoose.connect遗留openHandles的问题
问题核心原因
你遇到的openHandles问题本质是Jest的生命周期钩子没有等待异步操作完全执行完毕就进入了下一阶段,导致数据库连接建立、断开的操作没有完整跑完,留下了未释放的TLS连接句柄。你虽然写了connect和disconnect逻辑,但Jest没有等待这些异步操作完成就执行后续步骤,因此断开逻辑可能在连接还没完全建立的时候就执行了,无法正确清理所有句柄。
解决步骤
- 修正Jest异步钩子的写法,强制等待所有异步操作执行完成
把beforeAll、afterAll以及测试用例中的异步逻辑都加上async/await,确保Jest等待异步操作执行完成再推进生命周期,修正后的测试代码如下:const { startTestDB } = require("../conn"); const mongoose = require("mongoose"); const request = require("supertest"); const faker = require("faker"); const app = require("../app"); const fs = require("fs"); describe("test1: ", () => { beforeAll(async () => { try { await startTestDB() console.log("Connected to db."); } catch (e) { console.log(e); } }); afterAll(async () => { try { await mongoose.disconnect() console.log('db connection is closed'); } catch (e) { console.error(e); } }); it("Should save a new user", async () => { await request(app) .post("/save") .send({ name: faker.internet.userName(), email: faker.internet.email() }) .expect(201); }); }); - 优化mongoose连接配置,降低句柄残留概率
如果你使用的是MongoDB Atlas的srv连接串,可以在mongoose.connect的配置项中添加以下参数,避免TLS连接残留:mongoose.connect( `mongodb+srv://${mongodb.user}:${mongodb.password}@${mongodb.host}/${mongodb.db}-test?retryWrites=true&w=majority`, { useNewUrlParser: true, useUnifiedTopology: true, family: 4, // 强制使用IPv4解析,避免IPv6解析导致的TLS连接残留 poolSize: 1, // 测试环境仅保留1个连接,减少句柄生成 autoReconnect: false, // 测试场景不需要自动重连 connectTimeoutMS: 10000 } ) - 多测试文件场景统一管理数据库连接
如果项目中有多个测试文件都需要连接数据库,不要在每个文件单独处理连接和断开,而是在jest.config.js中配置setupFilesAfterEnv,全局统一在所有测试启动前建立连接,所有测试结束后断开连接,避免频繁建立断开连接产生的残留句柄。 - 优先使用本地内存数据库做单元测试
可使用mongodb-memory-server启动本地内存MongoDB实例进行测试,不需要走远程网络TLS连接,从根源上避免TLSWRAP类型的句柄残留,同时测试运行速度也会更快。
注意:如果完成以上修改后仍有openHandles提示,可检查是否有其他位置调用了mongoose.connect未被正确关闭,或者项目中是否存在未清理的全局定时器、未结束的网络请求。
内容的提问来源于stack exchange,提问作者Mohamed abdelmagid
相关产品推荐
相关产品推荐

