NestJS+TypeORM中repository.findOneOrFail不是函数的问题求助
NestJS + TypeORM 集成测试中
findOneOrFail 方法不存在的问题 业务背景
我正在用NestJS和TypeORM开发物业管理应用,核心逻辑:租户支付租金时创建RentPayment实体,触发Rent实体的创建或更新(Rent实体包含该租户的RentPayments列表),当支付总和等于总租金时生成RentPaymentReceipt。
现有代码实现
CreateRentService 代码
@Injectable() export class CreateRentService { constructor( @InjectRepository(Rent) private rentRepository: Repository<Rent>, @InjectRepository(RentPayment) private rentPaymentRepository: Repository<RentPayment>, @InjectRepository(Tenant) private tenantRepository: Repository<Tenant>, ) {} async execute(rentPaymentId: number) { const rentPayment = await this.rentPaymentRepository.findOneOrFail({ where: { id: rentPaymentId, }, }); const tenant = rentPayment.tenant; const rent = new Rent( tenant.userId, tenant, rentPayment.paymentDate.toISOString(), [rentPayment], ); return this.rentRepository.save(rent); } }
PropertyModule 配置
@Module({ imports: [TypeOrmModule.forFeature([RentPayment, Tenant, Property, Rent])], providers: [ RentPaymentService, CreateRentService, ] });
测试报错信息
运行集成测试时触发以下错误:
● Create rent › should create a rent when it does not already exist TypeError: this.rentPaymentRepository.findOneOrFail is not a function 18 | 19 | async execute(rentPaymentId: number) { > 20 | const rentPayment = await this.rentPaymentRepository.findOneOrFail({ | ^ 21 | where: { 22 | id: rentPaymentId, 23 | },
测试代码
describe('Create rent', () => { let typeOrmRentPaymentRepository: Repository<RentPayment>; let typeOrmTenantRepository: Repository<Tenant>; let typeOrmPropertyRepository: Repository<Property>; let typeOrmRentRepository: Repository<Rent>; let createRentService: CreateRentService; beforeEach(async () => { const module = await Test.createTestingModule({ imports: [ TypeOrmModule.forRoot(postgresqlTestDatasource.options), TypeOrmModule.forFeature([RentPayment, Tenant, Property, Rent]), ], providers: [ { provide: getRepositoryToken(RentPayment), useValue: Repository, }, { provide: getRepositoryToken(Tenant), useValue: Repository, }, { provide: getRepositoryToken(Property), useValue: Repository, }, { provide: getRepositoryToken(Rent), useValue: Repository, }, CreateRentService, ], }).compile(); typeOrmRentPaymentRepository = module.get<Repository<RentPayment>>( getRepositoryToken(RentPayment), ); typeOrmTenantRepository = module.get<Repository<Tenant>>( getRepositoryToken(Tenant), ); typeOrmPropertyRepository = module.get<Repository<Property>>( getRepositoryToken(Property), ); typeOrmRentRepository = module.get<Repository<Rent>>( getRepositoryToken(Rent), ); createRentService = module.get<CreateRentService>(CreateRentService); }); afterEach(async () => { await typeOrmRentRepository.clear(); await typeOrmRentPaymentRepository.clear(); await typeOrmTenantRepository.clear(); await typeOrmPropertyRepository.clear(); }); it('should create a rent when it does not already exist', async () => { // Given const userId = 32; const property = await typeOrmPropertyRepository.save(new Property(userId)); const tenant = await typeOrmTenantRepository.save( new Tenant(userId, property), ); const rentPayment = await typeOrmRentPaymentRepository.save( new RentPayment(tenant, 170000, new Date(2023, 0, 2, 7)), ); // When await createRentService.execute(rentPayment.id); // Then const expectedRent = new Rent(userId, tenant, '2023-01-01', [rentPayment]); const createdRents = await typeOrmRentRepository.find(); expect(createdRents.length).toEqual(1); expect(createdRents[0].tenant).toEqual(expectedRent.tenant); expect(createdRents[0].date).toEqual(expectedRent.date); expect(createdRents[0].rentPayments.length).toEqual(1); expect(createdRents[0].rentPayments[0]).toEqual(rentPayment); }); });
问题分析与解决方案
原因
测试模块中手动配置仓库provider时,将useValue设为了Repository类本身,而非实例化的仓库对象或模拟对象。TypeORM的findOneOrFail等方法都是实例方法,直接传入类的话,调用时自然找不到对应的方法。
修复方案
集成测试场景(使用真实仓库)
移除测试模块中手动覆盖仓库provider的配置,因为TypeOrmModule.forFeature已经会自动提供对应的仓库实例:
beforeEach(async () => { const module = await Test.createTestingModule({ imports: [ TypeOrmModule.forRoot(postgresqlTestDatasource.options), TypeOrmModule.forFeature([RentPayment, Tenant, Property, Rent]), ], providers: [CreateRentService], // 仅保留服务,无需手动提供仓库 }).compile(); // 直接获取TypeOrm自动注入的仓库实例 typeOrmRentPaymentRepository = module.get<Repository<RentPayment>>( getRepositoryToken(RentPayment), ); typeOrmTenantRepository = module.get<Repository<Tenant>>( getRepositoryToken(Tenant), ); typeOrmPropertyRepository = module.get<Repository<Property>>( getRepositoryToken(Property), ); typeOrmRentRepository = module.get<Repository<Rent>>( getRepositoryToken(Rent), ); createRentService = module.get<CreateRentService>(CreateRentService); });
单元测试场景(模拟仓库)
如果是单元测试需要模拟仓库行为,需创建包含模拟方法的对象,而非直接传入Repository类:
{ provide: getRepositoryToken(RentPayment), useValue: { findOneOrFail: jest.fn().mockResolvedValue(mockRentPayment), save: jest.fn().mockResolvedValue(mockRentPayment), }, }
内容的提问来源于stack exchange,提问作者gvann2
相关产品推荐
相关产品推荐

