Предотвращение не заключенного в act () Jest-предупреждение, когда обновление состояния не влияет на пользовательский интерфейс

Я пытаюсь выяснить, есть ли способ предотвратить предупреждение not wrapped in act (...), выданное 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();
});

Этот тест будет пройден, но я получу предупреждение not wrapped in act (...), потому что тест завершится до того, как getData() успеет закончиться.

В этом случае ответ от getData() устанавливает arr на то же значение (пустой массив), которое я изначально установил в верхней части компонента. Таким образом, мой пользовательский интерфейс не меняется после завершения асинхронной функции - я все еще просто смотрю на абзац, в котором нет элементов массива - поэтому у меня действительно нет ничего, что я мог бы утверждать, что ожидало бы завершения обновления состояния. .

Я могу 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.


person cjl750    schedule 19.02.2021    source источник


Ответы (1)


Вы можете использовать findByText (комбинацию getByText и waitFor), чтобы гарантировать, что все обновления произошли после разрешения утверждения.

it('renders', async () => {
    getData.mockResolvedValue({items: []});
    render(<MyComponent />);
    expect(await screen.findByText('no array items')).toBeInTheDocument();
});
person juliomalves    schedule 20.02.2021
comment
Спасибо. Я знал о findBy и много использовал его, но я не знал, что он обязательно будет ждать обновлений состояния. Документы, на которые я ссылался (testing-library.com/docs/ dom-testing-library / api-async) создается впечатление, что он просто ожидает асинхронных изменений DOM, но в этом случае DOM фактически не изменяется (даже если функция рендеринга компонента вызывается снова), так что я не думал, что это сработает. Я буду помнить об этом в будущем :) - person cjl750; 22.02.2021