简体   繁体   中英

Jest + react-testing-library: Warning update was not wrapped in act()

I am testing my component wit react-testing-library and test works well. I just can't get rid of this warning, fireEvent should by wrapped in act out-of-the-box, but I tried to wrap it again and it did'nt help.

Here is my test case.

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

Here is the warning

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 */

That issue is caused by many updates inside Component.

I got the same, this is how I solved the issue.

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

In the source code , fireEvent is already wrapped in act() .

The problem may be related to this issue, in which async logic (such as a useEffect ) is triggering state changes outside of fireEvent:

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

(Without seeing your component implementation it's hard to be sure if this is exactly what's happening in your case.)

Apparently there are plans to include async handling in a future release, so that this won't be a problem.

So this is hard to summarise but I'll try.

The act warning is simply telling you that there is something happening in your functional component which you're not testing.

Let's say we're rendering a list of todos like so

    <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>

The below test case will throw the act warning

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

But if you re-order the await lines like so

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

The act warning goes away. This makes sense. You get to make sure your users no longer see the loading indicator.

There are other cases so go ahead and read this post from kent Dodds.

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

add await before act

await act(() => {
      /* fire events that update state */
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM