Fix Warning in React: Update was not wrapped in act()
Published: 2021-03-06 19:48:22

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

#### 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`
```jsx
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>
  );
}
```
> ###### [View full file](https://github.com/il3ven/react-testing-devto/blob/not-fix/src/App.js)

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

![screely-1615056600912](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mtg6m2ni5bcc9hdj43d4.png)

`App.test.js`
```jsx
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();
});
```
> ###### [View full file](https://github.com/il3ven/react-testing-devto/blob/not-fix/src/App.test.js)

### 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`.

```diff
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');
});
```
> ###### [View full file](https://github.com/il3ven/react-testing-devto/blob/8b942e9f48a6ca886bfd5e5e1b052198a4bab8b9/src/App.test.js)

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.

```diff
- expect(screen.getByTestId('title').innerHTML).toBe('Title: delectus aut autem');
+ await waitFor(() => {
+  expect(screen.getByTestId("title").innerHTML).toBe(
+    "Title: delectus aut autem"
+  );
+ });
```
> ###### [View full file](https://github.com/il3ven/react-testing-devto/blob/fixed/src/App.test.js)

Now the tests work fine without any warnings. You can view the full working code at this [repository](https://github.com/il3ven/react-testing-devto).