Jest单元测试问题:调用父方法后如何验证子方法被调用?
Jest单元测试问题:调用父方法后无法检测子方法调用
问题概述
调用DefaultDatabaseService的deleteDatabase方法后,测试无法检测到deleteParkingReservation等子方法的调用,报错提示expect(jest.fn()).toHaveBeenCalled()期望调用次数≥1,但实际为0。
相关代码
DefaultDatabaseService.ts
async deleteDatabase(): Promise<string> { if (process.env.NODE_ENV === 'dev') { try { await this.deleteParkingReservation(); await this.deleteRoomReservation(); await this.deleteParkingSpace(); await this.deleteRoomSpace(); await this.deleteUser(); return `All database was deleted.`; } catch (error) { throw new NotFoundException('Could not delete database!', error); } } return 'You must use DEV environment for that!'; }
测试代码spec.ts
const modelStub: any = { exec: jest.fn().mockReturnThis(), deleteDatabase: jest.fn(), deleteMany: jest.fn().mockReturnThis(), deleteParkingReservation: jest.fn(), deleteRoomReservation: jest.fn().mockReturnThis(), deleteParkingSpace: jest.fn().mockReturnThis(), deleteRoomSpace: jest.fn().mockReturnThis(), deleteUser: jest.fn().mockReturnThis() }; describe('DefaultDatabaseService', () => { let service: DefaultDatabaseService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ DefaultDatabaseService, { provide: getModelToken('RoomReservationSpace'), useValue: modelStub }, { provide: getModelToken('ParkingReservationSpace'), useValue: modelStub }, { provide: getModelToken('RoomReservation'), useValue: modelStub }, { provide: getModelToken('ParkingReservation'), useValue: modelStub }, { provide: getModelToken('User'), useValue: modelStub } ] }).compile(); service = module.get<DefaultDatabaseService>(DefaultDatabaseService); }); it('should call deleteDatabase method', async () => { process.env.NODE_ENV = 'dev'; await service.deleteDatabase(); if (process.env.NODE_ENV === 'dev') { await expect(modelStub.deleteParkingReservation).toHaveBeenCalled(); } });
错误原因
你误解了方法的归属:deleteParkingReservation是DefaultDatabaseService类的实例方法,而非你注入的Model对象的方法。当前测试中断言的是modelStub.deleteParkingReservation被调用,但实际执行的是service实例自身的deleteParkingReservation方法,两者完全无关,所以断言失败。
修复方案
方案1:Mock并断言服务实例的子方法
通过jest.spyOnMock服务实例的子方法,直接断言这些方法是否被调用:
describe('DefaultDatabaseService', () => { let service: DefaultDatabaseService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ DefaultDatabaseService, // 保持原有的Model注入配置 { provide: getModelToken('RoomReservationSpace'), useValue: modelStub }, { provide: getModelToken('ParkingReservationSpace'), useValue: modelStub }, { provide: getModelToken('RoomReservation'), useValue: modelStub }, { provide: getModelToken('ParkingReservation'), useValue: modelStub }, { provide: getModelToken('User'), useValue: modelStub } ] }).compile(); service = module.get<DefaultDatabaseService>(DefaultDatabaseService); // Mock服务实例的子方法,避免执行真实逻辑 jest.spyOn(service, 'deleteParkingReservation').mockResolvedValue(); jest.spyOn(service, 'deleteRoomReservation').mockResolvedValue(); jest.spyOn(service, 'deleteParkingSpace').mockResolvedValue(); jest.spyOn(service, 'deleteRoomSpace').mockResolvedValue(); jest.spyOn(service, 'deleteUser').mockResolvedValue(); }); it('should call all sub methods when deleteDatabase runs in dev env', async () => { process.env.NODE_ENV = 'dev'; await service.deleteDatabase(); // 断言服务实例的子方法被调用 expect(service.deleteParkingReservation).toHaveBeenCalled(); expect(service.deleteRoomReservation).toHaveBeenCalled(); expect(service.deleteParkingSpace).toHaveBeenCalled(); expect(service.deleteRoomSpace).toHaveBeenCalled(); expect(service.deleteUser).toHaveBeenCalled(); }); });
方案2:断言Model的实际方法被调用(更推荐)
如果deleteParkingReservation等子方法的内部逻辑是调用注入的Model的deleteMany或其他方法,那么应该直接断言Model的方法是否被调用(这更贴近业务逻辑的验证):
假设deleteParkingReservation的实现如下:
async deleteParkingReservation() { await this.parkingReservationModel.deleteMany({}); }
修改测试断言:
it('should trigger Model delete methods when deleteDatabase runs in dev env', async () => { process.env.NODE_ENV = 'dev'; await service.deleteDatabase(); // 断言Model的deleteMany方法被调用 expect(modelStub.deleteMany).toHaveBeenCalled(); });
额外优化:环境变量的隔离
为了避免测试之间的环境变量污染,建议在测试前后重置环境变量:
beforeEach(() => { process.env.NODE_ENV = 'dev'; }); afterEach(() => { delete process.env.NODE_ENV; });
或者使用Jest的replaceProperty更安全地修改环境变量:
it('...', async () => { jest.replaceProperty(process.env, 'NODE_ENV', 'dev'); await service.deleteDatabase(); // 断言逻辑 });
内容的提问来源于stack exchange,提问作者LAURYNAS PETRAUSKAS
相关产品推荐
相关产品推荐

