如何进行在检查元素出现之前等待 5 秒的测试(React 测试库)

How to make a test that will wait 5 seconds before check element appearance (React testing lib)

在我的反应组件中,我有一个元素在 5 秒后出现。

我想做一个测试,用 jest fake timers 检查元素是否在 5 秒后出现,但不能让它工作...

我这里做错了什么?

其中一个示例不起作用:

it('check step 2 labels text, status "Submitted"', async () => {
    render(<ProgressIndicator appStatus="submitted" />);

    jest.advanceTimersByTime(5005);
    await waitFor(() => {
      expect(
        screen.getByText('Beep. Boop. Still doing our thing here.'),
      ).toBeInTheDocument();
    });

    await waitFor(() => {
      expect(screen.getByText('Verifying identity...')).toBeInTheDocument();
    });
  });

第二个例子:

it('check step 2 labels text, status "Submitted"', async () => {
    render(<ProgressIndicator appStatus="submitted" />);

    act(() => {
      jest.advanceTimersByTime(5005);
    });

    expect(
      screen.getByText('Beep. Boop. Still doing our thing here.'),
    ).toBeInTheDocument();
    expect(screen.getByText('Verifying identity...')).toBeInTheDocument();
  });

使用 await findBy 查询和 await waitFor 的一般经验法则是您应该使用

await findBy when you expect an element to appear but the change to the DOM might not happen immediately.

and await waitFor when you have a unit test that mocks API calls and you need to wait for your mock promises to resolve.

dom-testing-library.

的官方文档中也有提到

现在引起您的关注,您需要使用 jest.useFakeTimer() 启用假定时器,这将有助于模拟 setTimeout 和其他定时器函数。

official docs.

这里也提到了同样的情况

useFakeTimer 与 async methods 一起工作,但是,如果我们想使用像 getBy 查询这样的同步方法,那么我们必须使用 jest.advanceTimersByTime(5000) 来移动你的测试 5(任何指定的time) 秒。 (示例如下)

在下面的示例中,我重现了您提出的问题。我认为这可能会有所帮助。

import { useState } from 'react';
import { act, render, screen, waitForElementToBeRemoved } from '@testing-library/react';
import userEvent from '@testing-library/user-event';

const data = {
  name: 'subrato',
  age: 24,
};


function App() {
  const [myData, setState] = useState({});
  const [loading, setLoading] = useState(false);

  function clickHandler() {
    setLoading(true);
    setTimeout(() => {
      setState(data);
      setLoading(false);
    }, 5000);
  }

  return (
    <div className='App'>
      {loading && <div aria-label='loading'>loading....</div>}
      <p>{myData?.name}</p>
      <p>{myData?.age}</p>
      <button onClick={clickHandler}>Click me</button>
    </div>
  );
}
describe('Test my app', () => {
  beforeEach(() => {
    jest.useFakeTimers();
  });

  afterEach(() => {
    jest.useRealTimers();
  });

  it('display data', async () => {
    render(<App />);

    userEvent.click(screen.getByText('Click me'));

    expect(screen.getByLabelText(/loading/i)).toBeInTheDocument();

    expect(await screen.findByText('subrato')).toBeInTheDocument();
    expect(screen.getByText('24')).toBeInTheDocument();
   
  });

  it('display data second time', async () => {
    render(<App />);

    userEvent.click(screen.getByText('Click me'));

    expect(screen.getByLabelText(/loading/i)).toBeInTheDocument();

    act(() => jest.advanceTimersByTime(5000));

    expect(screen.getByText('subrato')).toBeInTheDocument();
    expect(screen.getByText('24')).toBeInTheDocument();
   
  });
});

测试结果

 PASS  src/TimerExample.spec.tsx (9.001 s)
  Test my app
    √ display data (487 ms)
    √ display data second time (35 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        14.997 s
Ran all test suites matching /TimerExample.spec.tsx/i.