Как использовать JEST для проверки хода рекурсивных функций - PullRequest
2 голосов
/ 14 мая 2019

Я пишу тест, используя JEST. Я не знаю, как проверить рекурсию обещаний в JEST.

В этом тесте функция повтора, которая выполняет рекурсию, является целью теста, пока не будет выполнено обещание.

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.

Ответы [ 2 ]

0 голосов
/ 14 мая 2019

Ваша функция очень трудна для тестирования с таймером.

Когда вы звоните 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

0 голосов
/ 14 мая 2019

Документация шуток предполагает, что тестирование обещаний довольно просто (https://jestjs.io/docs/en/asynchronous)

Они приводят этот пример (предположим, что fetchData возвращает обещание так же, как ваша функция повтора)

test('the data is peanut butter', () => {
  return fetchData().then(data => {
    expect(data).toBe('peanut butter');
  });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...