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

如何在NestJS中配置MongoDB内存服务器用于E2E测试?

问题分析

  1. 测试生命周期错误:afterEach中调用closeInMongodConnection会断开Mongoose连接并停止内存MongoDB服务器,但beforeAll仅初始化一次应用和模块,后续测试会因无数据库连接返回500错误。
  2. 配置冲突:AppModule可能已导入MongooseModule.forRoot指向外部数据库,测试时同时导入AppModule和rootMongooseTestModule会导致配置冲突,内存数据库未实际生效。

解决方案

1. 重构Mongoose测试模块,拆分资源管理逻辑

修改MongooseTestModule.ts,将数据清空与资源销毁分离,保留内存服务器在测试全程运行:

import { MongooseModule, MongooseModuleOptions } from '@nestjs/mongoose';
import { MongoMemoryServer } from 'mongodb-memory-server';
import { disconnect, connection } from 'mongoose';

let mongod: MongoMemoryServer;

export const rootMongooseTestModule = (options: MongooseModuleOptions = {}) =>
  MongooseModule.forRootAsync({
    useFactory: async () => {
      mongod = await MongoMemoryServer.create();
      const mongoUri = mongod.getUri();
      return {
        uri: mongoUri,
        ...options,
      };
    },
  });

// 仅清空所有集合数据,保留连接和服务器运行
export const clearDatabase = async () => {
  const collections = connection.collections;
  for (const key in collections) {
    await collections[key].deleteMany({});
  }
};

// 所有测试结束后销毁资源
export const closeInMongodConnection = async () => {
  await disconnect();
  if (mongod) await mongod.stop();
};

2. 调整测试文件的生命周期钩子

修改drive.e2e-spec.ts,将资源销毁移至afterAll,afterEach仅清空数据,同时确保内存数据库配置覆盖AppModule的默认配置:

import { HttpStatus, INestApplication } from '@nestjs/common';
import { getModelToken } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { Model } from 'mongoose';
import * as request from 'supertest';

import { AppModule } from '#app.module';
import { CreateDriveDto } from '#drive/dto/create-drive.dto';
import { Drive, DriveDoc } from '#drive/schema/drive.schema';
import {
  closeInMongodConnection,
  rootMongooseTestModule,
  clearDatabase,
} from 'test/util/MongooseTestModule';
import { dataFromJoi } from 'test/util/generate-valid-sample-data';
import { configTestApp } from 'test/util/setup-test-app';

const route = `/api/test/v2/drive`;
const validCreateData = dataFromJoi(CreateDriveDto);

let moduleFixture: TestingModule;
let app: INestApplication;
let mockDriveModel: Model<DriveDoc>;

beforeAll(async () => {
  moduleFixture = await Test.createTestingModule({
    imports: [
      // 先导入内存数据库模块,覆盖AppModule的Mongoose配置
      rootMongooseTestModule(),
      AppModule,
    ],
  }).compile();

  app = moduleFixture.createNestApplication();
  mockDriveModel = moduleFixture.get<Model<DriveDoc>>(getModelToken(Drive.name));
  configTestApp(app);
  await app.init();
});

// 每个测试后清空数据,避免测试间状态污染
afterEach(async () => {
  await clearDatabase();
});

// 所有测试完成后销毁资源
afterAll(async () => {
  await closeInMongodConnection();
  await app.close();
});

describe(`Integration Test :: Route ${route}`, () => {
  describe(`POST`, () => {
    test('Return 400 if request is invalid', async () => {
      await request(app.getHttpServer())
        .post(route)
        .send({})
        .expect(HttpStatus.BAD_REQUEST)
        .then((res) => {
          expect(res.body).toHaveProperty('statusCode');
          expect(res.body).toHaveProperty('message');
        });
    });

    test('Return 201 and create new entry if request is valid', async () => {
      await request(app.getHttpServer())
        .post(route)
        .send(validCreateData)
        .expect(HttpStatus.CREATED);
    });
  });

  describe(`GET`, () => {
    test('Return 200 and an array', async () => {
      await mockDriveModel.create(validCreateData);
      await request(app.getHttpServer())
        .get(route)
        .expect(HttpStatus.OK)
        .then((res) => {
          expect(Array.isArray(res.body)).toBeTruthy();
        });
    });
  });
});

3. 处理硬编码配置的情况

如果AppModule中直接硬编码了Mongoose连接地址,需要通过overrideModule强制替换配置:

moduleFixture = await Test.createTestingModule({
  imports: [AppModule],
})
  .overrideModule(MongooseModule)
  .useModule(rootMongooseTestModule())
  .compile();

其他测试策略建议

  • 模型模拟:单元测试中可使用jest.mock模拟Mongoose模型,跳过数据库操作;
  • 独立套件初始化:为每个测试套件单独创建应用和数据库实例,彻底隔离测试状态(会增加测试耗时);
  • 事务回滚:MongoDB 4.0+支持事务,可在测试后回滚操作替代清空集合,提升测试效率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 14:37:15