测试时,导致 React 状态更新的代码应包装到 act(...)

Kat*_*ber 2 reactjs jestjs react-testing-library

我正在测试我的组件

ForecastButtons.js

export const ForecastButtons = ({ city }) => {
  const [payload, setPayload] = useState(null)

  const getData = () => {
    fetchCityData(city).then((payload) => setPayload(payload));
  }
  const location = payload?.location?.name;
  const currentTemp = payload?.current?.temp_c;

  return(
    <div className="sm:col-span-2">
      <p className="block text-sm font-medium text-gray-700">Select forecast</p>
        <button onClick={getData} className="mt-1 bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded" type='button'>
          Today
        </button>
        <p key={city?.location?.id} className='my-5'>
          { location ? `Current weather in ${location} is ${currentTemp} degrees ` : 'Please search for city to see current weather'}
        </p>
    </div>
  )
}
Run Code Online (Sandbox Code Playgroud)

这是我的测试的一部分:

    test('render weather into component',  async () => {
    
      const { getByText } = render(<ForecastButtons weather={weatherResponce} city={'London'} />);
      const button = getByText('Today')
    
      await act(async () => {
        await fireEvent.click(button)
      })
      expect(getByText('London')).toBeInTheDocument();
    })
Run Code Online (Sandbox Code Playgroud)

请注意,这不是整个测试,只是不起作用的部分。错误如上。正如你所看到的,我向act()其中添加了 ,但它仍然不断抛出错误: When test, code that Causes React state update should bewrapped into act(...)

Kat*_*ber 5

我可以在React测试库和Medium上的act\xe2\x80\x9d错误中包含的\xe2\x80\x9cnot中找到一些提示,其中很多情况都得到了很好的解释。

\n

第一个有用的学习:

\n
\n

React 测试库已经act与其 API 集成。所以在大多数\n情况下,我们不需要将renderand包装fireEvent在 中act。例如\n:

\n
// With react-testing-library\nit("should render and update a counter", () => {\n  // Render a component\n  const { getByText } = render(<Counter />;\n  ...  \n\n  // Fire event to trigger component update\n  fireEvent.click(getByText("Save"));\n  ...\n});\n
Run Code Online (Sandbox Code Playgroud)\n
\n

就我而言,我得到了错误(我作为初学者的假设),因为要调用fireEvent.click触发器,这是一个异步调用。fetchData当它的响应返回时,fetchCityData/getData将被调用,但此时,更新将发生在 React\xe2\x80\x99s 调用堆栈之外。

\n

解决方案

\n

在断言之前,使用 waitFor 等待组件更新完全完成。waitFor是 React 测试库提供的 API,用于等待包装的断言在某个超时窗口内通过。

\n

我改变了我的测试代码如下:

\n
  test(\'renders responce into paragraph\', async () => {\n    render(<ForecastButtons weatherResponce={weatherResponce} city=\'London\' />);\n    const button = screen.getByRole(\'button\');\n    const label = screen.getByText(\'Please search for city to see current weather\');\n    fireEvent.click(button)\n    await waitFor(() => {\n      expect(label.textContent).toBe(`Current weather in ${weatherResponce.location.name} is ${weatherResponce.current.temp_c} degrees`);\n    });\n  })\n
Run Code Online (Sandbox Code Playgroud)\n

weatherResponce只是对模拟 HTTP 请求的模拟响应,这是我用 nock 做的。

\n