Jest react-testing-library: обновление с предупреждением не было включено в act()

#javascript #reactjs #jestjs #react-testing-library

#javascript #reactjs #jestjs #react-testing-library

Вопрос:

Я тестирую свой компонент с помощью react-testing-library, и тест работает хорошо. Я просто не могу избавиться от этого предупреждения, fireEvent должен быть завернут в act из коробки, но я попытался обернуть его снова, и это не помогло.

Вот мой тестовый пример.

 it.only("should start file upload if file is added to the field", async () => {
    jest.useFakeTimers();
    const { getByTestId } = wrapper;
    const file = new File(["filefilefile"], "videoFile.mxf");

    const fileInput = getByTestId("drop-zone").querySelector(
      "input[type='file']"
    );

    fireEvent.change(fileInput, { target: { files: [file] } });

    act(() => {
      jest.runAllTimers();
    });

    await wait(() => {
      expect(
        initialProps.uploadItemVideoFileConnect.calledWith(file, 123)
      ).toBe(true);
    });
  });
  

Вот предупреждение

 Warning: An update to UploadButtonGridComponent inside a test was not wrapped in act(...).

    When testing, code that causes React state updates should be wrapped into act(...):

    act(() => {
      /* fire events that update state */
    });
    /* assert on the output */
  

Комментарии:

1. Временный обходной путь, на который вы можете сослаться по ссылке github.com/testing-library/react-testing-library/issues /…

Ответ №1:

Эта проблема вызвана множеством обновлений внутри компонента.

Я получил то же самое, вот как я решил проблему.

 await act( async () => {
 fireEvent.change(fileInput, { target: { files: [file] } });
});
  

Комментарии:

1. Следует отметить, что если у вас есть два fireEvent прямо рядом друг с другом, вам нужно два act() , по одному для каждого.

2. Это дает мне Warning: You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);

3. Это решение решило мою проблему при тестировании компонента, который вводит данные в форму Formik. Я получал это предупреждение: Предупреждение: Обновление Formik внутри теста не было завернуто в act (…)

4. Это также было полезно при рендеринге компонента, который вызывал изменения внутреннего состояния

5. Я почти удалил Formik, пока не нашел это решение. Спасибо

Ответ №2:

В исходном коде оно fireEvent уже завернуто в act() .

Проблема может быть связана с этой проблемой, в которой асинхронная логика (такая как useEffect ) запускает изменения состояния за пределами fireEvent:

https://github.com/kentcdodds/react-testing-library/issues/281

(Не видя реализацию вашего компонента, трудно быть уверенным, что это именно то, что происходит в вашем случае.)

По-видимому, планируется включить асинхронную обработку в будущий выпуск, так что это не будет проблемой.

Комментарии:

1. Похоже, ваша проблема связана со следующим, пожалуйста, смотрите github.com/facebook/react/issues/14769

Ответ №3:

Так что это сложно обобщить, но я попытаюсь.

act Предупреждение просто сообщает вам, что в вашем функциональном компоненте происходит что-то, что вы не тестируете.

Допустим, мы визуализируем список задач следующим образом

     <ul>
      {loading ? (
        <p>Fetching todos</p>
      ) : (
        <>
          {appData.todoList.slice(0, 15).map((item) => {
            const { id, title } = item;
            return (
              <li key={id} data-testid={id}>
                <Link to={`/item/${id}`}>{title}</Link>
              </li>
            );
          })}
        </>
      )}
    </ul>
  

Приведенный ниже тестовый пример выдаст act предупреждение

 import { waitFor, screen, waitForElementToBeRemoved } from "@testing-library/react";

it("Renders <TodoList /> component", async () => {
    render(<TodoList />);
    await waitFor(() => expect(axios.get).toHaveBeenCalledTimes(1));
    await waitForElementToBeRemoved(() => screen.getByText(/Fetching todos/i));

    expect(axios.get).toHaveBeenCalledTimes(1);
    todos.slice(0, 15).forEach((td) => {
      expect(screen.getByText(td.title)).toBeInTheDocument();
    });
  });
  

Но если вы измените порядок await строк следующим образом

 await waitForElementToBeRemoved(() => screen.getByText(/Fetching todos/i));
await waitFor(() => expect(axios.get).toHaveBeenCalledTimes(1));
  

act Предупреждение исчезает. Это имеет смысл. Вы должны убедиться, что ваши пользователи больше не видят индикатор загрузки.

Есть и другие случаи, так что продолжайте и прочитайте этот пост от Кента Доддса.

https://kentcdodds.com/blog/fix-the-not-wrapped-in-act-warning

Комментарии:

1. Я не знаю, сработало ли это у меня или нет, но это отличный ответ! «Вы должны убедиться, что ваши пользователи больше не видят индикатор загрузки». <— yes

2. Спасибо за это. Отличное объяснение, и я смог исправить свои предупреждения.