【问题标题】:Preventing "not wrapped in act(...)" Jest warning when state update doesn't affect UI当状态更新不影响 UI 时防止“未包含在 act(...) 中”开玩笑警告
【发布时间】:2022-04-08 01:29:28
【问题描述】:

我试图弄清楚是否有一种方法可以防止 Jest/testing-library 抛出的“未包含在 act(...) 中”警告,当我在状态更新后没有任何东西可以断言时,导致警告发生,或者我是否应该忽略此警告。

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

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

此测试将通过,但我会收到“not Wrap in act(...)”警告,因为测试将在 getData() 有机会完成之前完成。

在这种情况下,来自getData() 的响应将arr 设置为与我最初在组件顶部设置的相同值(一个空数组)。因此,在异步函数完成后,我的 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/react,v11.2.2。

【问题讨论】:

    标签: javascript reactjs jestjs react-testing-library testing-library


    【解决方案1】:

    您可以使用findByTextgetByTextwaitFor 的组合)来确保在断言解决时所有更新都已发生。

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

    【讨论】:

    • 谢谢。我知道findBy 并且已经大量使用它,但我想我不知道它一定会等待状态更新。我一直在参考的文档 (testing-library.com/docs/dom-testing-library/api-async) 使它看起来只是在等待异步 DOM 更改,但在这种情况下,DOM 实际上并没有改变(即使组件的渲染函数确实被再次调用),所以我我想我不认为那会奏效。我必须记住这一点:)
    【解决方案2】:

    @juliomalves 的回答很到位。
    但是,我不得不把这个await 放在我的beforeEach 中:

    import {render, fireEvent} from '@testing-library/react';
    import MyComponent from './MyComponent';
    
    ...
    
    describe('MyComponent should', () => {
      let getByText, getByTestId, getAllByTestId, getByLabelText;
      beforeEach(async () => {
        let findByText;
        ({
          getByText,
          getByTestId,
          getAllByTestId,
          getByLabelText,
          findByText,
        } = render(<MyComponent {...props} />));
    
        // Have this here to avoid warnings because of setting state variables
        await findByText('no array items');
      })
    
      ...
    });
    

    【讨论】:

      猜你喜欢
      • 2019-08-06
      • 2020-08-29
      • 1970-01-01
      • 1970-01-01
      • 2020-12-13
      • 1970-01-01
      • 2021-05-21
      • 2021-01-14
      • 1970-01-01
      相关资源
      最近更新 更多