簡體   English   中英

當 state 更新不影響 UI 時防止“未包含在行為中(...)”開玩笑警告

[英]Preventing "not wrapped in act(...)" Jest warning when state update doesn't affect UI

我試圖弄清楚是否有一種方法可以防止 Jest/testing-library 拋出的“not wrapped in act(...)”警告,當我在導致警告發生的 state 更新后沒有什么可斷言時,或者我是否應該忽略此警告。

假設我有這個簡單的組件:

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 wrapped in act(...)”警告,因為測試將在getData()有機會完成之前完成。

在這種情況下,來自getData()的響應將arr設置為與我最初在組件頂部將其設置為相同的值(一個空數組)。 因此,在異步 function 完成后我的 UI 沒有改變——我仍然只是在看一段說“沒有數組項”的段落——所以我真的沒有什么可以斷言等待 state 更新去完成。

我可以expect(getData).toHaveBeenCalledTimes(1) ,但這不會等待 state 在 function 調用之后實際更新。

我試圖在測試中任意暫停以允許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。

您可以使用findByTextgetByTextwaitFor的組合)來確保在斷言解決時所有更新都已發生。

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

@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');
  })

  ...
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM