【问题标题】:How to properly use Promises and Timers with Jest如何在 Jest 中正确使用 Promises 和 Timers
【发布时间】:2020-03-13 22:56:48
【问题描述】:

我搜索了 SO 和 Google,发现了很多类似的问题和答案,但似乎都没有帮助我解决我的问题。

我正在尝试编写一些需要模拟异步轮询函数的测试用例。但无论我做什么,我都会得到:

在 jest.setTimeout.Timeout 指定的 5000 毫秒超时内未调用异步回调

我设置了一些最小的测试用例来重现问题:

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();
  });
});

谁能解释我做错了什么以及为什么?

【问题讨论】:

  • 调用jest.runAllTimers(); 之后 await 不能工作
  • @Bergi 之前它也无法正常工作,因为在调用 mock 之前不会有超时来打勾,对吧?
  • 你可能需要做类似const promise = mock(); jest.runAllTimers(); await promise;

标签: javascript async-await jestjs settimeout es6-promise


【解决方案1】:

在@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();
  });
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-17
    • 1970-01-01
    • 2015-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多