NestJS单元测试user.module.ts时如何传递MongoDB依赖
报错含义
这个报错本质是Nest测试容器在解析依赖时,找不到MongooseModule.forFeature()注册User模型所依赖的全局数据库连接实例DatabaseConnection。
正常业务代码里,你会在根模块AppModule里导入MongooseModule.forRoot()初始化数据库连接,这个连接会作为全局Provider注入到所有子模块的Mongoose上下文里。但你写单元测试时只单独导入了UserModule,测试容器里根本没有初始化过Mongoose根连接,UserModel拿不到需要的连接参数,自然就抛了依赖解析失败的错误。
你的判断是否准确
你的方向是对的,但表述不完全准确:你不需要单独注入Mongoose Model本身,而是要给测试环境补全Mongoose运行依赖的根数据库连接,或者直接Mock掉所有和真实数据库相关的依赖——毕竟模块编译的单元测试不需要真的连业务数据库。
具体解决方法
根据你的测试目的选对应方案即可:
方案1:用内存Mongo跑近真实的集成测试
如果你的测试后续还要验证数据库相关逻辑,用内存Mongo是最省事的方案,不需要改业务代码:
- 先安装
mongodb-memory-server依赖 - 在测试初始化时先启动内存Mongo实例,再把Mongoose根模块导入测试容器,连接内存库地址,最后再导入你的UserModule
参考代码:
import { Test } from '@nestjs/testing'; import { UserModule } from './user.module'; import { MongooseModule } from '@nestjs/mongoose'; import { MongoMemoryServer } from 'mongodb-memory-server'; // 原测试里写的ThingsModule是笔误,直接改成对应模块名 describe('UserModule', () => { let mongod: MongoMemoryServer; beforeAll(async () => { mongod = await MongoMemoryServer.create(); }); afterAll(async () => { await mongod.stop(); }); it('should compile the module', async () => { const module = await Test.createTestingModule({ imports: [ // 先初始化Mongoose根连接,指向内存库 MongooseModule.forRoot(mongod.getUri()), UserModule ], }).compile(); expect(module).toBeDefined(); }); });
方案2:Mock依赖跑纯单元测试(推荐,速度最快)
如果你只是为了覆盖user.module.ts的测试覆盖率,不需要验证数据库逻辑,直接Mock掉所有外部依赖就行,测试跑起来速度极快,也不需要额外装数据库相关的包:
import { Test } from '@nestjs/testing'; import { UserModule } from './user.module'; import { UserController } from './user.controller'; import { UserService } from './user.service'; import { HttpModule } from '@nestjs/axios'; import { Eauth } from './middlewares/eauth.middlewares'; describe('UserModule', () => { it('should compile the module', async () => { const module = await Test.createTestingModule({ imports: [HttpModule], controllers: [UserController], providers: [ UserService, // Mock Mongoose注入的UserModel { provide: 'UserModel', useValue: { find: jest.fn(), create: jest.fn(), findById: jest.fn(), updateOne: jest.fn(), deleteOne: jest.fn() } }, // Mock自定义中间件 { provide: Eauth, useValue: { use: jest.fn() } } ] }).compile(); expect(module).toBeDefined(); }); // 顺便覆盖configure中间件注册的逻辑,把覆盖率拉满 it('should register Eauth middleware for UserController', async () => { const moduleRef = await Test.createTestingModule({ imports: [UserModule] }) .overrideProvider('DatabaseConnection') .useValue({}) .overrideProvider('UserModel') .useValue({}) .compile(); const app = moduleRef.createNestApplication(); const applyMock = jest.fn().mockReturnThis(); const forRoutesMock = jest.fn(); const mockConsumer = { apply: applyMock.mockImplementation(() => ({ forRoutes: forRoutesMock })) }; const userModule = app.get(UserModule); userModule.configure(mockConsumer as any); expect(applyMock).toHaveBeenCalledWith(Eauth); expect(forRoutesMock).toHaveBeenCalledWith(UserController); }); });
额外提示:你原测试文件里describe的名称写的是
ThingsModule,属于复制代码留下的笔误,记得改成UserModule避免后续看测试报告混淆。
内容的提问来源于stack exchange,提问作者DavSev
相关产品推荐
相关产品推荐

