使用MikroORM的NestJS服务单元测试依赖注入失败问题排查
解决Nest+MikroORM单元测试中EntityRepository依赖注入失败问题
问题场景
我有一个基于MikroORM与MySQL正常协作的TestService,编写单元测试时,仅执行服务实例化的基础测试就报错。测试文件内已定义TestEntity、模拟Repository并配置测试模块,但运行时Nest提示无法解析TestService的EntityRepository依赖。
测试代码
import { Test, TestingModule } from '@nestjs/testing'; import { Entity, EntityRepository, Property, Unique } from '@mikro-orm/core'; import { getRepositoryToken } from '@mikro-orm/nestjs'; import { TestService } from './mikro-orm.service'; @Entity() export class TestEntity { @Property() @Unique() public Code: number; @Property({ length: 20 }) @Unique() public Description: string; constructor(Code: number, Description: string) { this.Code = Code; this.Description = Description; } } describe('TestService', () => { let service: TestService<TestEntity>; let serviceRepository: EntityRepository<TestEntity>; const REPOSITORY_TOKEN = getRepositoryToken(TestEntity); beforeEach(async () => { const mockDataRepository = { provide: REPOSITORY_TOKEN, useValue: { findAll: jest.fn(), findOne: jest.fn(), }, }; const module: TestingModule = await Test.createTestingModule({ providers: [TestService, mockDataRepository], }).compile(); service = module.get<TestService<TestEntity>>(TestService); serviceRepository = module.get<EntityRepository<TestEntity>>(REPOSITORY_TOKEN); }); it('should be defined', () => { expect(service).toBeDefined(); }); it('should be defined', () => { expect(serviceRepository).toBeDefined(); }); });
服务构造函数
constructor(private readonly DataEntity: EntityRepository<T_Entity>) {}
报错信息
TestService › should be defined Nest can't resolve dependencies of the TestService (?). Please make sure that the argument EntityRepository at index [0] is available in the RootTestModule context. Potential solutions: - If EntityRepository is a provider, is it part of the current RootTestModule? - If EntityRepository is exported from a separate @Module, is that module imported within RootTestModule? @Module({ imports: [ /* the Module containing EntityRepository */ ] }) at TestingInjector.lookupComponentInParentModules (../../node_modules/@nestjs/core/injector/injector.js:231:19) at TestingInjector.resolveComponentInstance (../../node_modules/@nestjs/core/injector/injector.js:184:33) at TestingInjector.resolveComponentInstance (../../node_modules/@nestjs/testing/testing-injector.js:16:45) at resolveParam (../../node_modules/@nestjs/core/injector/injector.js:106:38) at async Promise.all (index 0) at TestingInjector.resolveConstructorParams (../../node_modules/@nestjs/core/injector/injector.js:121:27) at TestingInjector.loadInstance (../../node_modules/@nestjs/core/injector/injector.js:52:9) at TestingInjector.loadProvider (../../node_modules/@nestjs/core/injector/injector.js:74:9) at async Promise.all (index 3) at TestingInstanceLoader.createInstancesOfProviders (../../node_modules/@nestjs/core/injector/instance-loader.js:44:9) Test Suites: 1 failed, 1 total Tests: 1 failed, 1 total
解决方案
核心问题是泛型类型擦除导致依赖注入令牌不匹配:Nest无法通过泛型EntityRepository<T_Entity>找到对应的实体Repository令牌,必须明确关联实体专属的注入令牌。
方案一:手动实例化服务(最简单直接)
跳过Nest的DI系统,直接手动创建服务实例并传入模拟的Repository:
describe('TestService', () => { let service: TestService<TestEntity>; let mockRepository: Partial<EntityRepository<TestEntity>>; beforeEach(() => { // 创建模拟Repository mockRepository = { findAll: jest.fn(), findOne: jest.fn(), }; // 手动实例化服务 service = new TestService(mockRepository as EntityRepository<TestEntity>); }); it('service should be defined', () => { expect(service).toBeDefined(); }); it('mock repository should be defined', () => { expect(mockRepository).toBeDefined(); }); });
方案二:修正测试模块的DI配置
如果要保留Nest测试模块的用法,需要通过useFactory明确关联依赖令牌:
beforeEach(async () => { const mockDataRepository = { provide: getRepositoryToken(TestEntity), useValue: { findAll: jest.fn(), findOne: jest.fn(), }, }; const module: TestingModule = await Test.createTestingModule({ providers: [ { provide: TestService, // 通过工厂函数手动关联依赖 useFactory: (repo) => new TestService<TestEntity>(repo), inject: [getRepositoryToken(TestEntity)], }, mockDataRepository, ], }).compile(); service = module.get<TestService<TestEntity>>(TestService); serviceRepository = module.get<EntityRepository<TestEntity>>(getRepositoryToken(TestEntity)); });
方案三:完善服务的依赖注入标记
在TestService的构造函数中添加@InjectRepository装饰器,明确指定实体类型,让Nest能正确识别注入令牌:
// 修改TestService的构造函数 import { InjectRepository } from '@mikro-orm/nestjs'; import { EntityRepository } from '@mikro-orm/core'; export class TestService<T_Entity> { constructor( @InjectRepository(T_Entity) private readonly DataEntity: EntityRepository<T_Entity> ) {} }
注:如果TestService是针对特定实体的服务,直接将T_Entity替换为具体的实体类(如TestEntity)即可。
核心原因
TypeScript的泛型类型在编译后会被擦除,Nest的DI系统无法通过泛型EntityRepository<T_Entity>匹配到对应的实体Repository令牌。必须通过getRepositoryToken(EntityClass)获取实体专属的令牌,或手动关联依赖关系。
内容的提问来源于stack exchange,提问作者Steven Scott
相关产品推荐
相关产品推荐

