Mocha+Supertest+MongoMemoryServer钩子配置异常问题咨询
问题核心原因
- 原配置中每个测试文件都主动调用
setup(),会重复启动Mongo内存实例并尝试用新的URI连接mongoose默认单例,触发重复连接报错。 - 将setup重命名为全局测试文件后只启动一次实例,但缺少测试间清理数据的逻辑,导致数据残留。
- 进程挂起多是因为Mongo连接未完全释放、测试环境下Express服务启动了端口监听未关闭,导致Node事件循环存在活跃句柄。
推荐解决方案(全局复用实例,性能最优)
该方案全局只启动一次Mongo内存实例,每个测试用例执行前清空所有集合,既保证数据隔离,又避免重复启动实例的性能消耗。
步骤1:修改setup.ts为全局钩子文件
import { MongoMemoryServer } from "mongodb-memory-server"; import mongoose from "mongoose"; let mongoServer: MongoMemoryServer; // 测试全局启动:创建Mongo内存实例并初始化连接 before(async () => { mongoServer = await MongoMemoryServer.create(); const mongoUri = mongoServer.getUri(); await mongoose.connect(mongoUri); }); // 每个测试用例执行前:清空所有集合数据,保证环境干净 beforeEach(async () => { const collections = mongoose.connection.collections; for (const collectionName in collections) { await collections[collectionName].deleteMany({}); } }); // 测试全局结束:释放所有资源 after(async () => { await mongoose.connection.dropDatabase(); await mongoose.disconnect(); await mongoServer.stop(); });
步骤2:配置测试运行器自动加载全局钩子
以Jest为例,在jest.config.js中添加配置,无需每个测试文件手动导入setup:
module.exports = { // 其他配置... setupFilesAfterEnv: ['./test/setup.ts'], // 可选:强制测试结束后退出进程,避免残留句柄挂起 forceExit: true, // 可选:排查未关闭的句柄原因 // detectOpenHandles: true }
步骤3:修改server.ts避免测试环境启动端口监听
Express端口监听会残留TCP句柄导致进程挂起,增加环境判断:
// 仅非测试环境启动端口监听 if (process.env.NODE_ENV !== 'test') { app.listen(3000, () => { console.log('服务启动成功') }) } export { app }
可选方案(每个测试文件完全独立隔离)
如果需要每个测试文件使用完全独立的Mongo实例和连接,可封装为可复用的工具函数:
// test/utils/setupTestDB.ts import { MongoMemoryServer } from "mongodb-memory-server"; import mongoose from "mongoose"; export const setupTestDB = () => { let mongoServer: MongoMemoryServer; before(async () => { mongoServer = await MongoMemoryServer.create(); const mongoUri = mongoServer.getUri(); // 创建独立连接实例,不占用全局mongoose单例 await mongoose.createConnection(mongoUri).asPromise(); }); beforeEach(async () => { const collections = mongoose.connection.collections; for (const collectionName in collections) { await collections[collectionName].deleteMany({}); } }); after(async () => { await mongoose.connection.dropDatabase(); await mongoose.disconnect(); await mongoServer.stop(); }); };
每个测试文件开头直接调用即可:
import { setupTestDB } from "../utils/setupTestDB"; setupTestDB(); // 剩余测试代码...
内容的提问来源于stack exchange,提问作者Nikola-Milovic
相关产品推荐
相关产品推荐

