用jest模拟setTimeout

b5buobof  于 2023-05-27  发布在  Jest
关注(0)|答案(2)|浏览(144)

我有一个用React.JS做的网站,它不断地为正在发生的一切(和什么都没有)发出事件。例如,用户在表单中输入了一些东西,发出事件。用户将焦点设置在一个字段上,并在一段时间内不做任何事情,我们再次发出一个事件。这个想法是为了了解客户的行为。
我有一个按钮,它曾经是<Button onClick={(e)=>handler(e)}/>。我不得不去抖按钮并将其更改为<Button onClick={(e)=>setTimeout(handler(e), 1000)}/>。否则,用户不明白发生了什么。
现在,我正在尝试调整测试,如下所示:

it('displays similar listings', async () => {
    const renderResult = renderVdp(renderParams);

    await new Promise((resolve) => setTimeout(resolve, 1000));
    await waitFor(async () => {
      expect(renderResult.getAllByText('2017 BMW M3')).toHaveLength(4);
    });
  });

通过在相关测试之前添加await new Promise((resolve) => setTimeout(resolve, 1000));,我能够保持原始测试的工作。如果没有它,测试将接收下一个发出的事件,该事件不是作为单击按钮的响应而发出的事件。
我想使用jest计时器模拟或类似的东西,而不是在我的测试中实际引入延迟。我试过使用jest.useFakeTimers(),但它不能像我需要的那样工作。我的期望落空了。
有什么建议吗

szqfcxe2

szqfcxe21#

一旦你使用了假计时器,你就可以控制何时运行它们。尝试在渲染后运行它们:

it('displays similar listings', async () => {
    jest.useFakeTimers()
    const renderResult = renderVdp(renderParams);
    jest.runAllTimers();
    expect(renderResult.getAllByText('2017 BMW M3')).toHaveLength(4);
  });

如果这不起作用,你可以使用一个更“积极”的mock:

global.setTimeout = jest.fn(cb => cb());

我会在测试设置beforeEach/beforeAll中执行此操作,并在测试拆卸afterEach/afterAll中回滚到原始setTimeout

zynd9foi

zynd9foi2#

如果我看不到代码,很难判断测试应该是什么样子。
如果你的组件有点像这样工作:

const SetTimeoutComponent = () => {
  const [content, setContent] = React.useState<string[]>([]);

  const handleButtonClick = (event: React.MouseEvent<HTMLElement>) => {
    setContent([
        '2017 BMW M3',
        '2017 BMW M3',
        '2017 BMW M3',
        '2017 BMW M3'
    ])
  }

  return (
    <div>
      <button onClick={(e)=>setTimeout(() => handleButtonClick(e), 1000)}>
        Set timeout with state update
      </button>
      <div>
        {content.map((item, index) => (
            <div key={index}>{item}</div>
        ))}
      </div>
    </div>
  );
};

然后你可以测试它如下:

it('displays similar listings', async () => {
  render(<TestedComponent />);
  const setTimeoutButton = screen.getByRole('button');

  jest.useFakeTimers();
  fireEvent.click(setTimeoutButton);
  act(() => {
    jest.runAllTimers();
  });
  expect(screen.getAllByText('2017 BMW M3')).toHaveLength(4);

  jest.useRealTimers();
});

您可以在my post中查看更多信息。

相关问题