当状态更新不影响用户界面时,防止未包装在动作(...)中的Jest警告(&C)

我正在尝试找出是否有方法可以防止在导致警告发生的状态更新后没有断言时,由Jest/Testing-Library引发的警告中没有包装";,或者我是否应该直接忽略此警告。

假设我有这样一个简单的组件:

import React, {useEffect, useState} from 'react';
import {getData} from 'services';

const MyComponent = () => {
  const [arr, setArr] = useState([]);

  useEffect(() => {
    (async () => {
      const {items} = await getData();
      setArr(items);
    })();
  }, []);

  return (
    <div>
      {!(arr.length > 0) && <p>no array items</p>}
      {arr.length > 0 && (
        <ul>
          {arr.map(item => (
            <li key={item.id}>{item.name}</li>
          ))}
        </ul>
      )}
    </div>
  );
};

export default MyComponent;

假设我只想测试一下,即使getData()没有为我返回任何数据,此组件也可以正常呈现。

所以我有一个这样的测试:

import React from 'react';
import {getData} from 'services';
import {render, screen} from 'testUtils';
import MyComponent from './MyComponent';

jest.mock('services', () => ({
  getData: jest.fn(),
}));

it('renders', () => {
  getData.mockResolvedValue({items: []});

  render(<MyComponent />);

  expect(screen.getByText('no array items')).toBeInTheDocument();
});

此测试将通过,但我将得到&out in act(...)";警告,因为测试将在getData()有机会完成之前完成。

在本例中,getData()的响应将arr设置为与我最初在组件顶部设置的值(空数组)相同的值。因此,在Async函数完成后,我的UI不会更改--我仍然在查看一个段落,上面写着&"没有数组项&"--所以我实际上没有什么可以断言的东西,可以等待状态更新完成。

我可以expect(getData).toHaveBeenCalledTimes(1),但这不会等待在函数调用后实际更新状态。

我已尝试在测试中任意暂停,以便有时间使setArr(items)发生:

it('renders', async () => {
  getData.mockResolvedValue({items: []});

  render(<MyComponent />);

  expect(screen.getByText('no array items')).toBeInTheDocument();
  
  await new Promise(resolve => setTimeout(resolve, 2000));

  expect(screen.getByText('no array items')).toBeInTheDocument();
});

但这似乎没有帮助,老实说,我也不确定为什么。

是否有办法通过仅修改测试来处理此情况?

我确信我可以通过重构MyComponent来解决这个问题,例如,通过将arr作为道具传递给MyComponent并将getData()调用移动到父组件,或者创建一些仅用于测试的自定义道具来完全跳过getData()调用,但我不想纯粹为了避免测试中的警告而修改组件。

我使用的是testing-library/reactv11.2.2。


解决方案

您可以使用findByText(getByTextwaitFor的组合)确保在断言解析时发生所有更新。

it('renders', async () => {
    getData.mockResolvedValue({items: []});
    render(<MyComponent />);
    expect(await screen.findByText('no array items')).toBeInTheDocument();
});

相关文章