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

Mocha+Supertest+MongoMemoryServer钩子配置异常问题咨询

问题核心原因

  1. 原配置中每个测试文件都主动调用setup(),会重复启动Mongo内存实例并尝试用新的URI连接mongoose默认单例,触发重复连接报错。
  2. 将setup重命名为全局测试文件后只启动一次实例,但缺少测试间清理数据的逻辑,导致数据残留。
  3. 进程挂起多是因为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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.23 20:45:01