When testing code that causes react state updates should be wrapped into act

When testing code that causes react state updates should be wrapped into act

React Testing Library and the “not wrapped in act” Errors

I recently upgraded React and React testing library. Very happy about the upgrade. But I start to see test errors like this:

Why? And how do I fix this?

When testing code that causes react state updates should be wrapped into act. Смотреть фото When testing code that causes react state updates should be wrapped into act. Смотреть картинку When testing code that causes react state updates should be wrapped into act. Картинка про When testing code that causes react state updates should be wrapped into act. Фото When testing code that causes react state updates should be wrapped into act

Why do I get this error?

In test, the code to render and update React components need to be included in React’s call stack. So the test behaves more similar to the user experience in real browsers.

Here is a simplified example from React’s document:

However, if your test still complains about “not wrapped in act(…)”, you might encounter one of these 4 cases described below.

Case 1: Asynchronous Updates

Test code somehow triggered a testing component to update in an asynchronous way. Here is an example:

The following test will have the “not wrapped in act” error:

fireEvent.click triggers fetchData to be called, which is an asynchronous call. When its response comes back, setPerson will be invoked, but at this moment, the update will happen outside of React’s call stack.

Case 2: Jest Fake Timers

When you have setTimeout or setInterval in your component:

… and use Jest’s fake timers to manipulate time:

…, unit test has no idea that advancing timers will cause component updates, and you will get the “not wrapped in act” error.

Wrap Jest’s timer manipulations in an act block, so test will know that advancing time will cause component to update:

Case 3: Premature Exit

Your test prematurely exits before components finish rendering or updating.

This normally happens in components that have loading state, e.g. components fetching data using GraphQL or REST.

The test checks if “Loading …” is present.

However, the `it` block exits before the loading state disappears and data comes back. This kind of test will also cause “not wrapped in act” errors.

Make sure the test exits after all the rendering and updates are done. To do that, we can wait for the loading state to disappear:

Case 4: Formik Updates

This is actually another variation of Case 1. It goes like this: test simulates events to change values in form inputs, e.g. changing value in a text input. If the form input is managed by Formik, your test will have a chance to run into “not wrapped in act” errors.

Similar to Case 1, wait for all updates to complete, then perform assertions:

Conclusion

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.

Here is the warning

When testing code that causes react state updates should be wrapped into act. Смотреть фото When testing code that causes react state updates should be wrapped into act. Смотреть картинку When testing code that causes react state updates should be wrapped into act. Картинка про When testing code that causes react state updates should be wrapped into act. Фото When testing code that causes react state updates should be wrapped into act

3 Answers 3

Trending sort

Trending sort is based off of the default sorting method — by highest score — but it boosts votes that have happened recently, helping to surface more up-to-date answers.

It falls back to sorting by highest score if no posts are trending.

Switch to Trending sort

That issue is caused by many updates inside Component.

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

When testing code that causes react state updates should be wrapped into act. Смотреть фото When testing code that causes react state updates should be wrapped into act. Смотреть картинку When testing code that causes react state updates should be wrapped into act. Картинка про When testing code that causes react state updates should be wrapped into act. Фото When testing code that causes react state updates should be wrapped into act

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

(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

The below test case will throw the act warning

But if you re-order the await lines like so

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.

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

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

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

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.

When testing code that causes react state updates should be wrapped into act. Смотреть фото When testing code that causes react state updates should be wrapped into act. Смотреть картинку When testing code that causes react state updates should be wrapped into act. Картинка про When testing code that causes react state updates should be wrapped into act. Фото When testing code that causes react state updates should be wrapped into act

Exit fullscreen mode

View full file

Why the above test does not work?

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.

Exit fullscreen mode

View full file

Exit fullscreen mode

View full file

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

When testing, code that causes React state updates should be wrapped into act

I have this test:

The test passes but I get the following warning:

Warning: An update to TestApp inside a test was not wrapped in act(. ).

console.error node_modules/react-dom/cjs/react-dom.development.js:506 Warning: An update to TestApp inside a test was not wrapped in act(. ).

When testing code that causes react state updates should be wrapped into act. Смотреть фото When testing code that causes react state updates should be wrapped into act. Смотреть картинку When testing code that causes react state updates should be wrapped into act. Картинка про When testing code that causes react state updates should be wrapped into act. Фото When testing code that causes react state updates should be wrapped into act

6 Answers 6

Trending sort

Trending sort is based off of the default sorting method — by highest score — but it boosts votes that have happened recently, helping to surface more up-to-date answers.

It falls back to sorting by highest score if no posts are trending.

Switch to Trending sort

The key is to await act and then use async arrow function.

Keep calm and happy coding

When testing code that causes react state updates should be wrapped into act. Смотреть фото When testing code that causes react state updates should be wrapped into act. Смотреть картинку When testing code that causes react state updates should be wrapped into act. Картинка про When testing code that causes react state updates should be wrapped into act. Фото When testing code that causes react state updates should be wrapped into act

Try using await inside act

I solved the problem in this way,you can try it.

I was getting the same issue which gets resolved by using async queries (findBy*) instead of getBy* or queryBy*.

Async query returns a Promise instead of element, which resolves when an element is found which matches the given query. The promise is rejected if no element is found or if more than one element is found after a default timeout of 1000ms. If you need to find more than one element, use findAllBy.

But as you know it wont work properly if something is not on screen. So for queryBy* one might need to update test case accordingly

[Note: Here there is no user event just simple render so findBy will work otherwise we need to put user Event in act ]

When testing code that causes react state updates should be wrapped into act

Imagine you have a component like this:

Here’s the code for that (if you were to do it with class components, don’t worry a function version is coming later in the post):

So, let’s imagine we want to write a test for this. Here’s what you might write to verify that the component is working properly:

Great! So now, if we make some sort of typo and not call the updateUsername function or we forget to call it with the new username then our test will fail and it provides us value.

But, what if we rewrite this to a function component with hooks? Let’s try that:

And the cool thing about React Testing Library is because it’s free of implementation details we can run those exact same tests with this refactored version of our component.

The dreaded act(. ) warning

Unfortunately, if we try that, we’ll get this really annoying warning in the console that looks like this:

What the!? So what’s going on here? Well, you might not have noticed, but our test didn’t test our component’s happy-path fully and left one important aspect vulnerable to regressions. Let’s go back to the class version and comment out an important line of code:

Guess what! Our tests still pass! But, the Saving. loading indicator next to our submit button never goes away:

Oh no! It sure would’ve been nice if we’d had some warning that we weren’t testing all that our component was doing so we could’ve written a good test from the start. Wait. Is that what act was yelling at us about? YES IT WAS!

So the act warning from React is there to tell us that something happened to our component when we weren’t expecting anything to happen. So you’re supposed to wrap every interaction you make with your component in act to let React know that we expect our component to perform some updates and when you don’t do that and there are updates, React will warn us that unexpected updates happened. This helps us avoid bugs like the one described above.

Luckily for you and me, React automatically handles this for any of your code that’s running within the React callstack (like click events where React calls into your event handler code which updates the component), but it cannot handle this for any code running outside of it’s own callstack (like asynchronous code that runs as a result of a resolved promise you are managing or if you’re using jest fake timers). With those kinds of situations you typically need to wrap that in act(. ) or async act(. ) yourself. BUT, React Testing Library has async utilities that are wrapped in act automatically!

How to fix the act(. ) warning

So, let’s first thank the React team for letting us know that we didn’t test everything that’s happening in our component (thanks React team! 🙏) and then let’s add an assertion to our test to cover the use case we forgot the first time around:

Great, so now we’re waiting for the saving text to be removed and that ensures that the saving text appears in the first place and is removed when saving is complete.

You’ll notice that this test passes whether we’re using class components or function components (+1 point for avoiding Testing Implementation Details), but without that extra line we only get the warning with function components not class components. The reason for this is because the React team couldn’t reasonably add this warning for class components without creating a TON of new warnings for people and their existing tests. So while the warning would probably help people find and fix bugs like this, the decision was made to only apply it to hooks so people would get the warning as they develop and test new components (+1 point for using function components over class components).

You can read more about React’s act utility here, but again, you shouldn’t have to use it very often. It’s built-into React Testing Library. There are very few times you should have to use it directly if you’re using React Testing Library’s async utilities.

If you’re still experiencing the act warning, then the most likely reason is something is happening after your test completes for which you should be waiting (like in our earlier examples).

An Alternative: waiting for the mocked promise

There’s one alternative I want to show you that isn’t quite as good for this use case, but could be useful, especially if there’s no visual indication of the async task completing.

Because our code waits for the updateUsername promise to resolve before continuing, we could return a promise from our fake version and use an async act to await that promise resolution:

Note that we’re manually calling act here and you can get that from react-dom/test-utils or React Testing Library re-exports it so you can get grab it from the import you already have. You’re welcome.

This isn’t preferable because it’s still not going to catch the bug we demonstrated earlier by commenting out that setState call, but it does make the warning go away properly. I’d just recommend making additional assertions about the way things look after the async action has completed.

Other use cases for manually calling act(. )

If you’re using all the React Testing Library async utilities and are waiting for your component to settle before finishing your test and you’re still getting act warnings, then you may need to use act manually. Here are a few examples:

1. When using jest.useFakeTimers()

Let’s say you have a component that’s checking against an API on an interval:

So we’ve handled all the edge cases and cleaned up after ourselves nicely, but when we write our test for it, we’re going to get the act warning again:

The act warning here is happening because of this line:

The tick function is happening outside of React’s callstack, so it’s unsure whether this interaction with the component is properly tested. React Testing Library does not have a utility for jest fake timers and so we need to wrap the timer advancement in act ourselves, like this:

And now the React act(. ) warning goes away!

Notice, that we can pull React DOM’s act testing utility directly from @testing-library/react because it’s simply re-exported by @testing-library/react (cool right!?).

2. When testing custom hooks

You’ll get an act(. ) warning with custom hooks when you call functions that are returned from your custom hook which result in state updates. Here’s a simple example of that:

Here, we’ll use @testing-library/react to validate our hook works:

You’ll notice that we’re pulling act from @testing-library/react and that’s because it simply re-exports the act function from react-test-renderer so you don’t need to add an additional export. Nice right!?

3. When using useImperativeHandle

You may not have even heard of this hook, and if you have you may not have run into this problem. That’s because you only run into this if you’re calling methods directly on a component which do internal state updates and you’re outside of React’s callstack. Let’s read through an example of act warnings popping up when testing components that use this hook.

So here’s a pretty contrived example, but you should be double-thinking your decisions any time you use this hook anyway:

Here’s how you might test this:

You’ll get an act warning on those increment and decrement calls because they’re happening outside the React callstack. So, let’s wrap them in act :

Conclusion

Hopefully you have a better understanding (and appreciation) for React’s (sometimes annoying but always right) act(. ) testing utility and the next time you get a warning you’re able to identify the source of the problem and fix it more quickly.

Again, most of the time you shouldn’t actually have to use act directly (thanks to React Testing Library’s async utilities), but when you do, you’ll know why it’s needed and fixes the problem.

Источники информации:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *