When to use act() in jest unit tests with react-dom
Asked Answered
W

4

76

According to the react unit testing documentation:

act()

To prepare a component for assertions, wrap the code rendering it and performing updates inside an act() call. This makes your test run closer to how React works in the browser.

But the test runs perfectly fine in both cases:

Without act()

it('Should return some text', () => {
  render(<TestComponent />, container);
  expect(container.textContent).toBe('some text');
});

With act()

it('Should return some text', () => {
  act(() => {
    render(<TestComponent />, container);
  });

  expect(container.textContent).toBe('some text');
})

The questions is: What exactly act() does, and when should someone use it?

Weaks answered 7/2, 2020 at 12:13 Comment(0)
C
43

From the act() docs:

When writing UI tests, tasks like rendering, user events, or data fetching can be considered as “units” of interaction with a user interface. React provides a helper called act() that makes sure all updates related to these “units” have been processed and applied to the DOM before you make any assertions

Further reading and examples: https://github.com/mrdulin/react-act-examples/blob/master/sync.md

Collect answered 11/2, 2020 at 9:26 Comment(4)
Why this piece of text is not already in the act() documentation?Mettah
Unfortunately the content of the link seems to be outdated. Most examples don't work anymore / work differently now. ( Details: Where the author claim act is needed, it's actually not needed. Where the author claims xxx will help, it doesn't)Inappropriate
@lin-du is there anything in your forked repo that wasn't in the original one? github.com/threepointone/react-act-examples/blob/master/sync.mdIridize
Would you say that act() waits for the useEffect to complete itself before applying the expect()?Birthwort
H
24

In simple terms:

ReactTestUtil's act() makes sure that anything that might take time - rendering, user events, data fetching - within it is completed before test assertions are run.

act(() => {
    // render components
    });
    // make assertions

If you're using a library like React Testing Library, then things like the render function are already wrapped in act(), and you generally will not need to use it explicitly.

Note that React Testing Library does have it's own act() function, which is simply a wrapper for ReactTestUtil's act(). This may be useful in asynchronous state updates, though other React Testing Library tools like waitFor may be better.

Hagen answered 25/8, 2021 at 19:37 Comment(2)
If you have async code inside it you need to add await act(async () => {Cheese
It seems await is only required when the handle is async. The code also contains warnings when not used properly. github.com/facebook/react/blob/main/packages/react-dom/src/… > react/blob/29fbf6f62625c4262035f931681c7b7822ca9843/packages/react/index.js#L33 > react/blob/29fbf6f62625c4262035f931681c7b7822ca9843/packages/react/src/ReactClient.js > react/blob/29fbf6f62625c4262035f931681c7b7822ca9843/packages/react/src/ReactAct.jsAerometer
B
3

Basically, act() is used to simulate like react actually works. But if you use render from @testing-library/react, it comes with act() implicitly. You can read more about it in the documentation.

Bussy answered 20/6, 2021 at 16:58 Comment(0)
T
2

I dont have in-depth knowledge on this topic, following are just my personal understandings

in short

  • when you call codes like render()/ some code that trigger a state update,
    you wrap those codes inside act().
    act() will make sure all the states are updated to the Dom tree & the useEffect are executed, before act() is done

  • if there are async function invocation inside your useEffect, calling act() is insufficient.
    you (also) need to manually add some timer to wait for the async operation, eg: waitFor / findBy after the act()

  • sometimes, you dont have to use act(), you can just use eg: waitFor / findBy

  • in React Testing Library, things like render and fireEvent are already wrapped in act, so dont wrap them again

reference

Titograd answered 30/11, 2023 at 21:29 Comment(1)
Yeah, I have an useState set* function inside useEffect. Thanks for enlightening me!Liturgics

© 2022 - 2024 — McMap. All rights reserved.