NestJS+Jest中expect().toHaveReturnedWith()断言返回空对象问题
NestJS + Jest 中
toHaveReturnedWith() 断言失败问题 在NestJS项目中使用Jest测试时,遇到expect().toHaveReturnedWith()断言异常:通过jest.spyOn(subscriptionsHistoryRepository, 'findAll').mockResolvedValue([mockRecords, 2])设置mock返回值后,调试可见findAll()确实返回预期的mockRecords,但用expect(subscriptionsHistoryRepository.findAll).toHaveReturnedWith([mockRecords, 2])断言时收到空对象{},其余断言均正常通过。
测试代码
describe('SubscriptionsModule', () => { describe('GetSubscriptionHistoryService', () => { let service: GetSubscriptionHistoryService; let subscriptionsHistoryRepository: SubscriptionsHistoryRepository; beforeAll(async () => { const moduleRef: TestingModule = await Test.createTestingModule({ providers: [ GetSubscriptionHistoryService, { provide: SubscriptionsHistoryRepository, useValue: { findAll: jest.fn(), }, }, ], }).compile(); service = moduleRef.get<GetSubscriptionHistoryService>(GetSubscriptionHistoryService); subscriptionsHistoryRepository = moduleRef.get<SubscriptionsHistoryRepository>( SubscriptionsHistoryRepository, ); }); afterEach(() => { jest.resetAllMocks(); }); it('Should have all class instances defined', () => { expect(service).toBeDefined(); expect(subscriptionsHistoryRepository).toBeDefined(); }); it("Should return the subscription's status history", async () => { jest.spyOn(subscriptionsHistoryRepository, 'findAll').mockResolvedValue([mockRecords, 2]); await service.execute(CUSTOMER_ID, SUBSCRIPTION_ID, 1); expect(subscriptionsHistoryRepository.findAll).toHaveBeenCalledTimes(1); expect(subscriptionsHistoryRepository.findAll).toHaveBeenCalledWith({ where: { customerId: CUSTOMER_ID, subscriptionId: SUBSCRIPTION_ID, }, take: 5, skip: 0, order: { createdAt: 'DESC', }, }); expect(subscriptionsHistoryRepository.findAll).toHaveReturnedWith([mockRecords, 2]); }); }); });
测试结果
expect(jest.fn()).toHaveReturnedWith(expected) Expected: [[{"createdAt": 2023-11-30T02:02:51.982Z, "customerId": "f1e8fc48-4780-45e4-858b-9e15b3382db5", "deletedAt": null, "extProviderStatus": "trialing", "id": "2e4c42b1-3d46-4438-bc68-64de46aa16d0", "status": "started", "subscriptionId": "2e4c42b1-3d46-4438-bc68-64de46aa16d5", "updatedAt": 2023-11-30T02:02:51.982Z}, {"createdAt": 2023-11-30T02:02:51.982Z, "customerId": "f1e8fc48-4780-45e4-858b-9e15b3382db5", "deletedAt": null, "extProviderStatus": "active", "id": "2e4c42b1-3d46-4438-bc68-64de46aa16d1", "status": "active", "subscriptionId": "2e4c42b1-3d46-4438-bc68-64de46aa16d5", "updatedAt": 2023-11-30T02:02:51.982Z}], 2] Received: {}
原因分析
toHaveReturnedWith()断言的是同步函数的直接返回值,但findAll是异步方法,实际返回的是一个Promise对象,而非你通过mockResolvedValue设置的resolved结果。Jest在记录异步mock函数的返回值时,会将Promise对象的状态记录下来,但toHaveReturnedWith无法解析Promise,所以拿到的是空对象(Promise的内部状态快照)。
解决方案
方案1:断言业务方法的返回值(推荐)
直接测试service.execute()的返回结果,这更贴合业务测试的核心目标:
it("Should return the subscription's status history", async () => { jest.spyOn(subscriptionsHistoryRepository, 'findAll').mockResolvedValue([mockRecords, 2]); const result = await service.execute(CUSTOMER_ID, SUBSCRIPTION_ID, 1); expect(subscriptionsHistoryRepository.findAll).toHaveBeenCalledTimes(1); expect(subscriptionsHistoryRepository.findAll).toHaveBeenCalledWith({ where: { customerId: CUSTOMER_ID, subscriptionId: SUBSCRIPTION_ID, }, take: 5, skip: 0, order: { createdAt: 'DESC', }, }); // 直接断言service的返回值 expect(result).toEqual([mockRecords, 2]); });
方案2:检查mock函数的resolved结果
如果一定要验证mock函数的resolved值,可以通过mock.results获取并解析Promise:
it("Should return the subscription's status history", async () => { const mockFindAll = jest.spyOn(subscriptionsHistoryRepository, 'findAll').mockResolvedValue([mockRecords, 2]); await service.execute(CUSTOMER_ID, SUBSCRIPTION_ID, 1); expect(subscriptionsHistoryRepository.findAll).toHaveBeenCalledTimes(1); expect(subscriptionsHistoryRepository.findAll).toHaveBeenCalledWith({ where: { customerId: CUSTOMER_ID, subscriptionId: SUBSCRIPTION_ID, }, take: 5, skip: 0, order: { createdAt: 'DESC', }, }); // 取出mock的resolved值并断言 const resolvedValue = await mockFindAll.mock.results[0].value; expect(resolvedValue).toEqual([mockRecords, 2]); });
方案3:放弃toHaveReturnedWith
明确toHaveReturnedWith不适用于异步函数,改用上述两种方式替代。
内容的提问来源于stack exchange,提问作者bikerdurge
相关产品推荐
相关产品推荐

