NestJS使用Jest测试嵌套异步函数,Delete调用验证失败
问题分析与解决方案
问题描述
在为NestJS的UpdateService编写Jest测试时遇到异常:服务实际运行可正常删除数据,测试覆盖率也显示Delete相关代码已执行,但测试断言HolidayDatesService.Delete的调用次数始终为0,而HolidayIdsService.GetOne的调用次数断言正常通过。
核心业务逻辑:RemoveInvalidHolidayDateEntriesForAYear方法遍历指定年份的假期记录,调用ValidateHolidayIdActive_验证有效性,无效则调用DeleteOneHolidayDateRecord_执行删除。
核心原因
问题出在RemoveInvalidHolidayDateEntriesForAYear方法中的forEach循环使用了异步回调函数:
forEach本身不支持异步等待,它会立即遍历完所有元素,不会等待每个回调里的await ValidateHolidayIdActive_和await DeleteOneHolidayDateRecord_执行完成。- 这导致
RemoveInvalidHolidayDateEntriesForAYear方法会提前返回,测试代码中的await service.RemoveInvalidHolidayDateEntriesForAYear(2022)会在所有Delete操作实际执行前结束,此时断言Delete的调用次数自然为0。但后续异步任务仍会执行,所以控制台能看到Delete的日志,实际运行也能删除数据。
解决方案
需要确保RemoveInvalidHolidayDateEntriesForAYear方法等待所有异步删除操作完成后再返回,有两种可行方式:
方式一:使用for...of循环替代forEach
for...of支持异步等待,能确保每个迭代的异步操作完成后再进入下一次循环:
async RemoveInvalidHolidayDateEntriesForAYear(Year: number): Promise<void> { let ExistingHolidayDates: Array<HolidayDatesResponseDTO>; try { ExistingHolidayDates = await this.HolidayDateDataService_.GetAllRecordsByYear(Year); } catch (Exception) { return; } // 替换forEach为for...of for (const OneDateEntry of ExistingHolidayDates) { const HolidayIdIsActive: boolean = await this.ValidateHolidayIdActive_(OneDateEntry.HolidayId); if (!HolidayIdIsActive) { await this.DeleteOneHolidayDateRecord_(OneDateEntry); } } return; }
方式二:使用Promise.all批量等待异步任务
将所有异步回调包装成Promise数组,通过Promise.all等待全部完成:
async RemoveInvalidHolidayDateEntriesForAYear(Year: number): Promise<void> { let ExistingHolidayDates: Array<HolidayDatesResponseDTO>; try { ExistingHolidayDates = await this.HolidayDateDataService_.GetAllRecordsByYear(Year); } catch (Exception) { return; } // 收集所有异步任务并等待完成 await Promise.all(ExistingHolidayDates.map(async (OneDateEntry: HolidayDatesResponseDTO) => { const HolidayIdIsActive: boolean = await this.ValidateHolidayIdActive_(OneDateEntry.HolidayId); if (!HolidayIdIsActive) { await this.DeleteOneHolidayDateRecord_(OneDateEntry); } })); return; }
修改后,测试代码中的await service.RemoveInvalidHolidayDateEntriesForAYear(2022)会等待所有Delete操作执行完毕,此时断言HolidayDatesService.Delete的调用次数就能得到正确结果。
内容的提问来源于stack exchange,提问作者Steven Scott
相关产品推荐
相关产品推荐

