如何使用JEST来测试递归函数的时间过程

时间:2019-05-14 07:05:22

标签: javascript typescript jestjs

我用JEST编写了一个测试。我不知道如何在JEST中测试Promise递归。

在此测试中,执行递归的重试功能是测试的目标,直到承诺被解决为止。

export function retry<T>(fn: () => Promise<T>, limit: number = 5, interval: number = 1000): Promise<T> {
  return new Promise((resolve, reject) => {
    fn()
      .then(resolve)
      .catch((error) => {
        setTimeout(() => {
          // Reject if the upper limit number of retries is exceeded
          if (limit === 1) {
            reject(error);

            return;
          }
          // Performs recursive processing of callbacks for which the upper limit number of retries has not been completed
          try {
            resolve(retry(fn, limit - 1, interval));
          } catch (err) {
            reject(err);
          }
        }, interval);
      });
  });
}

对上述重试功能执行以下测试。

  1. retry()在第三次运行中解决。第一次,第二次和第三次分别每1000秒调用一次。

我想用JEST编写这些代码如下。


jest.useFakeTimers();

describe('retry', () => {
  // Timer initialization for each test
  beforeEach(() => {
    jest.clearAllTimers();
  });
  // Initialize timer after all tests
  afterEach(() => {
    jest.clearAllTimers();
  });

  test('resolve on the third call', async () => {
    const fn = jest
      .fn()
      .mockRejectedValueOnce(new Error('Async error'))
      .mockRejectedValueOnce(new Error('Async error'))
      .mockResolvedValueOnce('resolve');

    // Test not to be called
    expect(fn).not.toBeCalled();
    // Mock function call firs execution
    await retry(fn);
    // Advance Timer for 1000 ms and execute for the second time
    jest.advanceTimersByTime(1000);
    expect(fn).toHaveBeenCalledTimes(2);
    // Advance Timer for 1000 ms and execute for the third time
    jest.advanceTimersByTime(1000);
    expect(fn).toHaveBeenCalledTimes(3);

    await expect(fn).resolves.toBe('resolve');
  });

});

结果,它失败并出现以下错误。

● retry › resolve on the third call
Timeout - Async callback was not invoked within the 30000ms timeout specified by jest.setTimeout.Error: 

    > 16 |   test('resolve on the third call', async () => {
         |   ^
      17 |     jest.useFakeTimers();
      18 |     const fn = jest
      19 |       .fn()

我认为,在关于此错误的JEST设置中,这是可以管理的。但是,从根本上讲,我不知道如何在JEST中测试Promise递归处理。

2 个答案:

答案 0 :(得分:0)

开玩笑的文档表明测试承诺非常简单(a note

他们给出了这个示例(假设fetchData像您的重试功能一样正在返回一个承诺)

test('the data is peanut butter', () => {
  return fetchData().then(data => {
    expect(data).toBe('peanut butter');
  });
});

答案 1 :(得分:0)

使用计时器很难测试您的功能。

当您调用await retry(fn);时,这意味着您将等到retry返回一个值,但是setTimeout被阻止,直到您调用jest.advanceTimersByTime(1000); =>这是主要原因,因为从未调用过jest.advanceTimersByTime(1000);

您可以看到我的示例,它可以与开玩笑的假计时器一起正常工作。

  test("timing", async () => {
    async function simpleTimer(callback) {
      await callback();
      setTimeout(() => {
        simpleTimer(callback);
      }, 1000);
    }

    const callback = jest.fn();
    await simpleTimer(callback); // it does not block any things
    for (let i = 0; i < 8; i++) {
      jest.advanceTimersByTime(1000); // then, this line will be execute
      await Promise.resolve(); // allow any pending jobs in the PromiseJobs queue to run
    }
    expect(callback).toHaveBeenCalledTimes(9);  // SUCCESS
  });

我认为,您可以跳过测试计时器的详细信息,只需测试一下您的逻辑即可:fn被调用了3次,最后返回了"resolve"

test("resolve on the third call", async () => {
    const fn = jest
      .fn()
      .mockRejectedValueOnce(new Error("Async error"))
      .mockRejectedValueOnce(new Error("Async error"))
      .mockResolvedValueOnce("resolve");

    // expect.assertions(3);

    // Test not to be called
    expect(fn).not.toBeCalled();
    // Mock function call firs execution

    const result = await retry(fn);

    expect(result).toEqual("resolve");

    expect(fn).toHaveBeenCalledTimes(3);
  });

注意:删除所有假计时器-jest.useFakeTimers