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

5

36

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 */
Veterinarian answered 15/3, 2019 at 10:54 Comment(1)
A temporary workaround you can refer the link github.com/testing-library/react-testing-library/issues/…Hessite
U
53

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] } });
});
Uhland answered 21/1, 2020 at 10:48 Comment(8)
To note that if you have two fireEvent right next to each other, you need two act(), one for each.Hake
This gives me 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 () => ...);Casing
This solution solved my issue when testing a component that types into a Formik form. I was getting this warning: Warning: An update to Formik inside a test was not wrapped in act(...)Westsouthwest
This was also helpful when rendering a component that triggered internal state changesSilky
I almost removed Formik until I found this solution. Thank youAllheal
This answer is incorrect. fireEvent already uses act internally. All you're doing by adding await act... is letting the promises tick through once more. You can achieve the same thing by taking the fireEvent call back out of the act and adding await act(()=>{}); because this will also let one level of promises resolve. If your component has another promise resolution, you'll have the same problem.Syrup
@lmat-ReinstateMonica - Reinstate Monica You lost me. Could you post a code line?Allred
I think this would be equivalent to the answer: fireEvent.change(fileInput, ...); await act(()=>{});. Sorry, this stuff is very confusing. Good luck out there!Syrup
C
16

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.

Champlin answered 15/3, 2019 at 19:48 Comment(1)
Your issue seems like related to following, please see github.com/facebook/react/issues/14769Buff
H
15

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

Hedelman answered 12/6, 2020 at 14:26 Comment(2)
I don't know if this worked for me or not, but this is an excellent answer! "You've got to make sure your users no longer see the loading indicator." <-- yesMvd
Thanks for this. Great explanation and I was able to fix my warnings.Val
G
0

Shortened version of @jaredsk answer:

await act(() => fireEvent.submit(form));

Since async do not have to be define while returning promise

Grapher answered 5/10, 2023 at 9:17 Comment(0)
L
0

I had a strange bug where it was giving this error and freezing for a subcomponent and the error was thrown in the parent's render function in the test.

I was simply missing brackets[]s in a useEffect.

Lucius answered 16/11, 2023 at 18:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.