【问题标题】:NestJS using Jest test async functions within async functionsNestJS 在异步函数中使用 Jest 测试异步函数
【发布时间】:2022-11-10 14:50:05
【问题描述】:

我正在尝试为我的服务编写一个测试,并且一切正常(服务,到目前为止的测试),但我无法验证调用了一个函数,这失败了。但是,测试的代码覆盖率输出表明该行正在执行,如果我自己运行代码,它确实会删除数据。

我试图仅复制服务处理的相关部分来说明我的问题。我的服务维护来自外部服务的假期日期。这将删除错误条目,并每年添加新的假期日期记录。我已将其缩减为我遇到问题的功能。

export class UpdateService {
    constructor(
        private readonly HolidayDateDataService_: HolidayDatesService,
    ) {
        this.RemoveInvalidHolidayDateEntriesForAYear(Year);
    }

    async RemoveInvalidHolidayDateEntriesForAYear(Year: number): Promise<void> {
        let ExistingHolidayDates: Array<HolidayDatesResponseDTO>;

        try {
            ExistingHolidayDates = await this.HolidayDateDataService_.GetAllRecordsByYear(Year); // From external API
        } catch (Exception) {
            return;
        }

        ExistingHolidayDates.forEach( async (OneDateEntry: HolidayDatesResponseDTO) => {
            const HolidayIdIsActive: boolean = await this.ValidateHolidayIdActive_(OneDateEntry.HolidayId); // Validates Entry should be maintained.  
            if (! HolidayIdIsActive) {
                await this.DeleteOneHolidayDateRecord_(OneDateEntry);
            }
        });
        return;
    }

    async DeleteOneHolidayDateRecord_(HolidayDateData: HolidayDatesResponseDTO): Promise<void> {
        console.log('Start DeleteOneHolidayDateRecord_');
        try {
            console.log('Start HolidayDateDataService');
            await this.HolidayDateDataService_.Delete(
                new HolidayDatesEntity(HolidayDateData.HolidayId, HolidayDateData.Name, HolidayDateData.Year),
                HolidayDateData.Key_
            );
            console.log('End HolidayDateDataService');
        } catch (Exception) {
            ; // Do nothing, try the next one
        }
        console.log('End DeleteOneHolidayDateRecord_');
        return;
    }

    async ValidateHolidayIdActive_(HolidayId: string): Promise<boolean> {
        console.log('Start ValidateHolidayIdActive_');
        try {
            console.log('Start HolidayIdDateService');

            const HolidayIdData: HolidayIdsResponseDTO = await this.HolidayIdDataService_.GetOne(HolidayId);

            console.log('End HolidayIdDateService');
            if (HolidayIdData && HolidayIdData.Active) {
                return true;
            }
        } catch (Exception) {
            ; // Do nothing as the method ends with a return false
        }
        console.log('End ValidateHolidayIdActive_');
        return false;
    }
}

正如我所说,所有这些都有效。但是,我正在尝试测试并验证是否调用了删除。

describe('UpdateService', () => {
    let service: UpdateService;
    let HolidayIdsTestService: HolidayIdsService;
    let HolidayDatesTestService: HolidayDatesService;

    const HolidayRecords: Array<HolidaysApi> = new Array<HolidaysApi>(
        { id: 31, name: 'Christmas Day', observedDate: '2022-12-27' } as HolidaysApi,
        { id: 32, name: 'Boxing Day', observedDate: '2022-12-28' } as HolidaysApi,
    );


    beforeEach(async () => {
        const mockHolidayIdsService = {
            provide: HolidayIdsService,
            useValue: {
                GetOne: jest.fn(),
            },
        };

        const mockHolidayDatesService = {
            provide: HolidayDatesService,
            useFactory: () => ({
                Delete: jest.fn(),
                GetAllRecordsByYear: jest.fn(),
            }),
        };

        const module: TestingModule = await Test.createTestingModule({
            providers: [
                UpdateService,
                mockHolidayDatesService,
                mockHolidayIdsService,
            ],
        }).compile();

        service = module.get<UpdateService>(UpdateService);
        HolidayIdsTestService = module.get<HolidayIdsService>(HolidayIdsService);
        HolidayDatesTestService = module.get<HolidayDatesService>(HolidayDatesService);
    });

    it('should delete HolidayDate records if ValidateHolidayIdActive_ returns false', async () => {
        jest.spyOn(HolidayDatesTestService, GetAllRecordsByYear').mockResolvedValue(HolidayDateRecords);
    jest.spyOn(HolidayIdsTestService, 'GetOne').mockResolvedValue(new HolidayIdsResponseDTO('Fake_Holiday_Id', false, 31, 'Christmas Day'));
    jest.spyOn(HolidayDatesTestService, 'Delete').mockResolvedValue();

    await service.RemoveInvalidHolidayDateEntriesForAYear(2022);
    expect(HolidayIdsTestService.GetOne).toHaveBeenCalledTimes(2);
    expect(HolidayDatesTestService.Delete).toHaveBeenCalledTimes(2);
});

我的测试中的问题是最后一个期望失败:

expect(jest.fn()).toHaveBeenCalled()

Expected number of calls: >= 1
Received number of calls:    0

  532 |             await service.RemoveInvalidHolidayDateEntriesForAYear(2022);
  533 |             expect(HolidayIdsTestService.GetOne).toHaveBeenCalledTimes(2);
> 534 |             expect(HolidayDatesTestService.Delete).toHaveBeenCalledTimes(2);
      |                                                    ^                                                                      

我遇到的问题是 ValidateHolidayIdActive_ 被调用了 2 次(它在内部使用 HoldiayIdsTestService,这是被模拟的),这是我所期望的,并且因为它被模拟返回 False 状态,所以 DeleteOneHolidayDateRecord_ 应该被调用 2 次也是。但是,在调用验证内部服务时,.toHaveBeenCalled().toHaveBeenCalledTimes(2) 都失败了,这又被模拟了。

但是,通过代码中的 console.log() 调用,我可以看到模块以正确的顺序调用,一切都开始了,然后完成了整个链。

记录#1

console.log Start ValidateHolidayIdActive_
console.log Start HolidayIdDateService
console.log Start ValidateHolidayIdActive_
console.log Start HolidayIdDateService

console.log End HolidayIdDateService
console.log End ValidateHolidayIdActive_
console.log End HolidayIdDateService
console.log End ValidateHolidayIdActive_

记录#2

console.log   Start DeleteOneHolidayDateRecord_
console.log   Start HolidayDateDataService
console.log   Start DeleteOneHolidayDateRecord_
console.log   Start HolidayDateDataService

console.log   End HolidayDateDataService
console.log   End DeleteOneHolidayDateRecord_
console.log   End HolidayDateDataService
console.log   End DeleteOneHolidayDateRecord_

它似乎确实可以正确调用并完成。但是,一个功能被监视为正常工作,而另一个则没有。我无法理解我的错误。

感谢您提供任何帮助,因为它应该是一个简单的错误,但完全让我感到沮丧,而且我似乎找不到它。我最初在一个函数中拥有更多的功能,并且像这样分解它来测试每个函数(它适用于相同风格的模拟)。

【问题讨论】:

    标签: typescript jestjs nestjs


    【解决方案1】:

    我的问题与我正在做的处理有关。 await/async 不能运行具有回调的循环,例如 forEach()。使用 for..of 格式,将纠正我遇到的错误。它需要对我编写的代码进行一些重构,但它确实解决了这个问题。

    ...
    ExistingHolidayDates.forEach( async (OneDateEntry: HolidayDatesResponseDTO) => {
        const HolidayIdIsActive: boolean = await 
        this.ValidateHolidayIdActive_(OneDateEntry.HolidayId); // Validates Entry should be maintained.  
        if (! HolidayIdIsActive) {
            await this.DeleteOneHolidayDateRecord_(OneDateEntry);
            }
        });
    });
    

    将处理更改为不是使用 forEach 的回调,而是改为 for..of:

    for (OneDateEntry of ExistingHolidayDates) => {
        const HolidayIdIsActive: boolean = await 
        this.ValidateHolidayIdActive_(OneDateEntry.HolidayId); // Validates Entry should be maintained.  
        if (! HolidayIdIsActive) {
            await this.DeleteOneHolidayDateRecord_(OneDateEntry);
            }
        });
    });
    
    

    这将允许适当的处理。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-08
      • 2015-04-25
      • 2012-08-22
      相关资源
      最近更新 更多