javascript - 如何使用JEST测试递归函数的时程

标签 javascript typescript jestjs

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

在这个测试中,执行递归的重试函数是测试的目标,直到 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 递归处理。

最佳答案

你的函数很难用定时器测试。

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

你可以看到我的例子,它与 jest 的假计时器一起工作得很好。

  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

关于javascript - 如何使用JEST测试递归函数的时程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56124733/

相关文章:

javascript - meteor react 计算未在 collection.update() 上触发

javascript - 为什么 ZeroClipboard 在第二次单击时起作用?

node.js - 如何部署 Angular 通用

reactjs - useRef 仅在初始化时只读值

javascript - toLocaleDateString 使用相同的语言是不同的

javascript - 如何访问嵌套文档中的某个字段

javascript - 如果输入值为空,则使用 Javascript 分配一个值 "empty"

visual-studio - 我在哪里可以找到 tsconfig.json 默认值?

typescript - 使用 Jest 测试 typescript 异步功能

javascript - 判断函数是否接受参数