Fix Warning in React: Update was not wrapped in act()

il3ven

il3ven

Posted on March 6, 2021

Fix Warning in React: Update was not wrapped in act()

The purpose of this article is to fix the following error.



Warning: An update to App 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 */

This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act


Enter fullscreen mode Exit fullscreen mode

Why am I seeing this?

In my experience this happens because your component performs some state change after the test has finished running.

How to fix this?

Add more expect statements to expect the change in state. This makes sure that tests do not finish before.


Example

Let's look at a code that shows the act() warning and then try to fix that code.

Bad Code

App.js



function App() {
  const [todo, setTodo] = useState("");

  useEffect(() => {
    const fetch = async () => {
      const _todo = (
        await axios.get(`https://jsonplaceholder.typicode.com/todos/1`)
      ).data;
      setTodo(_todo.title);
    };

    fetch();
  }, []);

  return (
    <div className="App">
      <h1>React App</h1>
      <h2 data-testid="title">Title: {todo}</h2>
    </div>
  );
}


Enter fullscreen mode Exit fullscreen mode
View full file

Here we fetch some data from API in useEffect then setState to show the data in a h2 tag.

screely-1615056600912

App.test.js



jest.mock("axios");

test("renders learn react link", () => {
  axios.get.mockImplementation(() => {
    return {
      data: {
        userId: 1,
        id: 1,
        title: "delectus aut autem",
        completed: false,
      },
    };
  });

  render(<App />);

  expect(screen.getByText(/React App/i)).toBeInTheDocument();
});


Enter fullscreen mode Exit fullscreen mode
View full file

Why the above test does not work?

React Testing Library renders <App/> component. After the initial render it expects React App to be present. Since, it is present the test will finish.

useEffect is called after the initial render and calls setTodo to set state. But the tests have finished until then.

Maybe after setting the state something changes and your tests start failing but that would not be tested. So, you should not be ignoring these warnings.

Good code

To fix the above error you should make sure tests finish after all the state changes have been done.

To do this we add the following line in the above App.test.js.



test("renders learn react link", () => {
  axios.get.mockImplementation(() => {
    return {
      data: {
        userId: 1,
        id: 1,
        title: "delectus aut autem",
        completed: false,
      },
    };
  });

  render(<App />);

  expect(screen.getByText(/React App/i)).toBeInTheDocument();
+ expect(screen.getByTestId('title').innerHTML).toBe('Title: delectus aut autem');
});


Enter fullscreen mode Exit fullscreen mode
View full file

The above alone will not work. This is because the expect runs before the state has been set. To solve this problem we can use waitFor from @testing-library/react. This will wait for whatever is inside it to be true or timeout.



- expect(screen.getByTestId('title').innerHTML).toBe('Title: delectus aut autem');
+ await waitFor(() => {
+  expect(screen.getByTestId("title").innerHTML).toBe(
+    "Title: delectus aut autem"
+  );
+ });


Enter fullscreen mode Exit fullscreen mode
View full file

Now the tests work fine without any warnings. You can view the full working code at this repository.

💖 💪 🙅 🚩
il3ven
il3ven

Posted on March 6, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related