Как правильно использовать Обещания и Таймеры с Jest - PullRequest
0 голосов
/ 13 марта 2020

Я искал и SO, и Google и нашел много похожих вопросов и ответов, но ни один из них, похоже, не помог мне решить мою проблему.

Я пытаюсь написать несколько тестовых случаев, где мне нужно высмеивать асин c функцию опроса. Но независимо от того, что я делаю, я получаю:

Asyn c обратный вызов не был вызван в течение времени ожидания 5000 мс, указанного в jest.setTimeout.Timeout

Я настроил несколько минимальных тестовых примеров, чтобы воспроизвести проблему:

jest.useFakeTimers();

describe('timers test', () => {
  it('plain timer works as expected', () => {
    const mock = jest.fn();
    setTimeout(mock, 5000);

    jest.runAllTimers();
    expect(mock).toHaveBeenCalled();
  });

  it('Using a timer to mock a promise resolution results in a jest timeout error', async () => {
    const mock = jest.fn(() => {
      return new Promise((resolve) => setTimeout(resolve, 500));
    });

    const handler = jest.fn();

    await mock().then(handler);

    jest.runAllTimers();

    expect(handler).toHaveBeenCalled();
  });

  it('Using a timer to mock a promise rejection results in a jest timeout error', async () => {
    const mock = jest.fn(() => {
      return new Promise((resolve, reject) => setTimeout(reject, 500));
    });

    const handler = jest.fn();

    await mock().catch(handler);

    jest.runAllTimers();

    expect(handler).toHaveBeenCalled();
  });
});

Может кто-нибудь объяснить, что я делаю неправильно и почему?

1 Ответ

0 голосов
/ 13 марта 2020

Так что с последующим комментарием от @Bergi я объяснил, что done на самом деле тоже не было необходимости. Мне просто нужно было переупорядочить некоторые вещи. Затем я столкнулся с аналогичной проблемой, когда тестировал цепочки обещаний, которые еще более подчеркивали это, поэтому я добавил несколько случаев для этого. Комментарий

jest.useFakeTimers();

describe('timers test', () => {
  it('Using a plain timer works as expected', () => {
    const mock = jest.fn();
    setTimeout(mock, 5000);

    jest.runAllTimers();
    expect(mock).toHaveBeenCalled();
  });

  it('Using a timer to mock a promise resolution', async () => {
    const mock = jest.fn(() => {
      return new Promise((resolve) => setTimeout(resolve, 500));
    });

    const handler = jest.fn();

    const actual = mock().then(handler);
    jest.runAllTimers();
    await actual;

    expect(handler).toHaveBeenCalled();
  });

  it('Using a timer to mock a promise rejection', async () => {
    const mock = jest.fn(() => {
      return new Promise((resolve, reject) => setTimeout(reject, 500));
    });

    const handler = jest.fn();

    const actual = mock().catch(handler);
    jest.runAllTimers();
    await actual;

    expect(handler).toHaveBeenCalled();
  });

  it('Using a timer to mock a promise resolve -> delay -> resolve chain', async () => {
    const mockA = jest.fn(() => {
      return Promise.resolve();
    });

    const mockB = jest.fn(() => {
      return new Promise((resolve, reject) => {
        setTimeout(resolve, 500);
      });
    });

    const handler = jest.fn();

    const actual = mockA()
      .then(() => {
        const mockProm = mockB();
        jest.runAllTimers();
        return mockProm;
      })
      .then(handler);

    jest.runAllTimers();
    await actual;

    expect(mockA).toHaveBeenCalled();
    expect(mockB).toHaveBeenCalled();
    expect(handler).toHaveBeenCalled();
  });

  it('Using a timer to mock a promise resolve -> delay -> reject chain', async () => {
    const mockA = jest.fn(() => {
      return Promise.resolve();
    });

    const mockB = jest.fn(() => {
      return new Promise((resolve, reject) => {
        setTimeout(reject, 500);
      });
    });

    const handler = jest.fn();

    const actual = mockA()
      .then(() => {
        const mockProm = mockB();
        jest.runAllTimers();
        return mockProm;
      })
      .catch(handler);


    await actual;

    expect(mockA).toHaveBeenCalled();
    expect(mockB).toHaveBeenCalled();
    expect(handler).toHaveBeenCalled();
  });
});

@ Bergi привел меня к решению. В итоге я использовал функцию done и удалил await. Кажется, это работает, по крайней мере, в этом минимальном тестовом примере.

jest.useFakeTimers();

describe('timers test', () => {
  it('plain timer works as expected', () => {
    const mock = jest.fn();
    setTimeout(mock, 5000);

    jest.runAllTimers();
    expect(mock).toHaveBeenCalled();
  });

  it('Using a timer to mock a promise resolution results in a jest timeout error', async (done) => {
    const mock = jest.fn().mockImplementation(() => {
      return new Promise((resolve) => setTimeout(resolve, 500));
    });

    // make the handler invoke done to replace the await    
    const handler = jest.fn(done);

    mock().then(handler);
    jest.runAllTimers();

    expect(handler).toHaveBeenCalled();
  });

  it('Using a timer to mock a promise rejection results in a jest timeout error', async (done) => {
    const mock = jest.fn().mockImplementation(() => {
      return new Promise((resolve, reject) => setTimeout(reject, 500));
    });

    // make the handler invoke done to replace the await
    const handler = jest.fn(done);

    mock().catch(handler);
    jest.runAllTimers();

    expect(handler).toHaveBeenCalled();
  });
});
...