如何在Nestjs中为CronJob编写单元测试

问题描述 投票:0回答:2

我在开玩笑地为下面的代码片段编写单元测试时遇到困难:

async addCronJob(props: IAddAndUpdateCronJobDetails) {
   const {name, individualSchedule} = props;
   const parsedCronTime = convertDateAndTimeToCron(
   individualSchedule.timeOfRun,
   individualSchedule.dateOfrun
   )

  const {jobType, dateOfRun, id, timeOfRun} = individualSchedule;

  const newJob = new CronJob(
   parsedCronTime,
   async () => {
   return this.sqsService.getSqsApproval({
   //some properties
    }).then(() => {
    //some logic
    })
   },
   null,
   false,
   'Asia/Singapore'
  )

 this.schedulerRegistry.addCronJob(name, newJob)
 newJob.start()
}

这是我的单元测试:

//at the top
jest.mock('cron', () => {
const mScheduleJob = {start: jest.fn(), stop: jest.fn()};
const mCronJob = jest.fn(() => mScheduleJob);
return {CronJob: mCronJob}
})

***************

describe('addCronJob', () => {
 it('should add a new cron job', async (done) => {
  const testFn = jest.fn();
  const parsedCronTime = convertDateAndTimeToCron(
   mockSchedule.timeOfRun,
   mockSchedule.dateOfrun
   )
  const testCronJob = new CronJob(
  parsedCronTime,
  testFn,
  null,
  false,
  'Asia/Singapore'
  );
 
 return dynamicCronService.addCron({//properties}).then(() => {
   expect(CronJob).toHaveBeenCalledWith(//properties);
   expect(testCronJob.start).toBeCalledTimes(1);
   done()
 })
 })

})

以上测试通过,没有错误。但是,它无法在 cron 作业本身内测试此异步代码块:

async () => {
   return this.sqsService.getSqsApproval({
   //some properties
    }).then(() => {
    //some logic
    })
 }

有人知道如何测试上面的代码块吗?

谢谢!

unit-testing jestjs cron nestjs
2个回答
1
投票

可能来晚了,但我自己也遇到了这个问题,想分享我的解决方案:

使用方法

  async addCronJob(taskName: string, cronEx: string, onTickCallback: () => void | Promise<void>): Promise<void> {
    const newJob = new CronJob(cronEx, onTickCallback);
    this.schedulerRegistry.addCronJob(taskName, newJob);
    newJob.start();
  }

测试

    it('should create cronJob', async () => {
      await service.addCronJob(jobName, testCronExpression, callbackFunction);

      expect(schedulerRegistryMock.addCronJob).toHaveBeenCalledWith(jobName, expect.any(CronJob));

      jest.advanceTimersByTime(60 * 60 * 1000);
      expect(callbackFunction).toHaveBeenCalled();
    });

我不必使用测试函数创建测试 cronjob,而是必须模拟我期望 cronjob 在滴答声上调用的实际函数(在您的情况下,我相信应该是 getSqsApproval)。然后我期望用 any CronJob 调用 SchedulerRegistry.addCronJob ,因为我不知道具体的作业。创建一个新工作并在这里期待它是行不通的。

最后,我把时间提前了1小时,因为我的testCronExpression是0 * * * *。您应该根据用于测试的 cron 表达式提前时间。 期望在时间过去后调用回调函数(实际上)对我有用。

希望这有帮助!


0
投票

@nik m的解决方案有效,但只需添加两点:

1.

.spec.ts
文件的模块级别添加以下行:

jest.useFakeTimers()

以我的单元测试实现为参考:

  async addRandomCronJob(
    name: string,
    task: (...params: any[]) => any | void,
    cronCommand?: CronCommand,
    options?: {
      days?: number;
      minutes?: number;
      seconds?: number; // Added seconds option
      weeks?: number;
      months?: number;
    },
  ): Promise<void> {
    const {
      days = 0,
      minutes = 0,
      seconds = 0, // Default seconds is set to 0
      weeks = 0,
      months = 0,
    } = options || {};

    let totalSeconds =
      days * 24 * 60 * 60 +
      minutes * 60 +
      weeks * 7 * 24 * 60 * 60 +
      months * 30 * 24 * 60 * 60 +
      seconds;

    if (totalSeconds < 0) {
      throw new Error('At least one interval option must be greater than zero.');
    }

    if (totalSeconds == 0) {
      totalSeconds = 7 * 24 * 60 * 60;
    }

    const randomSeconds = Math.floor(Math.random() * totalSeconds);

    const cronExpression = `${randomSeconds} * * * * *`;

    const job = new CronJob(cronExpression, task, cronCommand);

    this.schedulerRegistry.addCronJob(name, job);
    this.logger.log(
      `Job ${name} added with a random cron timing within the specified intervals (every ${randomSeconds} seconds)!`,
    );
    job.start();
  }

describe('addRandomCronJob', () => {
    it('should add a cron job with a custom interval of 5 seconds', async () => { // Need to indicate the fn is `async`
      const name = 'testJob';
      const task = jest.fn(() => console.log("Task executed!"));
      const customInterval = { seconds: 5 };
      
      // Instead of creating a mock, use the actual service instance
      // Need to prepend the `await` keyword here
      await service.addRandomCronJob(name, task, undefined, customInterval);

      expect(task).not.toHaveBeenCalled();
      expect(schedulerRegistry.addCronJob).toHaveBeenCalledWith(name, expect.any(CronJob));

      // https://stackoverflow.com/questions/71469500/how-to-write-a-unit-test-for-cronjob-in-nestjs
      jest.advanceTimersByTime(1000 * 1000);
      expect(task).toHaveBeenCalled();
    });
  });
© www.soinside.com 2019 - 2024. All rights reserved.