react-native-testing-library: Error thrown - Warning: You called act(async () => ...) without await.
Ask your Question
I have a simple test that seems to generate the right snapshot at the end of execution, but throws me a console.error
during the execution.
Here are the steps to get to the expected component state:
- Load the component
- Wait for the
useEffect
asynchronous logic to be executed so that theSegmentedControlIOS
is exposed (testID
=recordTypePicker
) - Set the
selectedSegmentIndex
to"1"
- Wait for the component to re-render and all the async logic to be executed
- Assert that the rendered component includes
newSObjectLayout
testID
The test
import * as React from 'react';
import { fireEvent, render, waitFor } from 'react-native-testing-library';
import { NewSObjectContainer } from '../NewSObject';
describe('NewSObjectContainer', () => {
const setup = () => {
const route = { params: { sobject: 'Account' } };
const navigation = { setOptions: jest.fn() };
const container = render(<NewSObjectContainer route={route} navigation={navigation} />);
return { container };
};
it('should render a NewSObjectContainer - with page layout exposed', async () => {
const { container } = setup();
await waitFor(() => expect(container.getByTestId('recordTypePicker')).toBeTruthy());
const input = container.getByTestId('recordTypePicker');
fireEvent(input, 'onChange', { nativeEvent: { selectedSegmentIndex: 1 } });
await waitFor(() => expect(container.getByTestId('newSObjectLayout')).toBeTruthy());
expect(container.toJSON()).toMatchSnapshot();
});
});
The console log
./node_modules/.bin/jest src/containers/__tests__/NewSObject.spec.tsx --u
console.error
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 () => ...);
at CustomConsole.console.error (node_modules/react-native/Libraries/YellowBox/YellowBox.js:63:9)
at printWarning (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:120:30)
at error (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:92:5)
at node_modules/react-test-renderer/cjs/react-test-renderer.development.js:14953:13
at tryCallOne (node_modules/promise/lib/core.js:37:12)
at node_modules/promise/lib/core.js:123:15
at flush (node_modules/asap/raw.js:50:29)
PASS src/containers/__tests__/NewSObject.spec.tsx
NewSObjectContainer
✓ should render a NewSObjectContainer - with page layout exposed (499ms)
› 1 snapshot written.
Snapshot Summary
› 1 snapshot written from 1 test suite.
Test Suites: 1 passed, 1 total
Tests: q passed, q total
Snapshots: 1 written, 1 passed, q total
Time: 4.865s, estimated 8s
Ran all test suites matching /src\/containers\/__tests__\/NewSObject.spec.tsx/i.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 49
- Comments: 112 (19 by maintainers)
Commits related to this issue
- [EditText] tests console error fixes Hint provided - https://github.com/callstack/react-native-testing-library/issues/379\#issuecomment-672932435 — committed to hyochan/dooboo-ui-legacy by hyochan 3 years ago
- Known issue: avoid replacing the global Promise object with PromiseJs (https://github.com/callstack/react-native-testing-library/issues/379#issuecomment-714341282) — committed to suman-vanan/stonks by suman-vanan 3 years ago
- extend react-native preset — committed to sbalay/without_await by sbalay 4 years ago
- Further clarify link picker test intent The assertions of these tests do not tell the full story of the test expectation. Namely, due to bugs within react-native-testing-library and its dependencies,... — committed to WordPress/gutenberg by dcalhoun 3 years ago
- [RNMobile] Upgrade to RN 0.66 (#36328) * Upgrade base React Native packages for 0.66.2 - react-native - react - react-dom - react-test-renderer - metro-react-native-babel-preset - metro-react... — committed to WordPress/gutenberg by dcalhoun 2 years ago
- replace waitFor with act wherever possible This avoids the rather clumsy and obnoxious warning from https://github.com/callstack/react-native-testing-library/issues/379 — committed to mmomtchev/react-native-settings by mmomtchev 2 years ago
- solve that obnoxious `await act(async)` problem Refs: https://github.com/callstack/react-native-testing-library/issues/379 — committed to mmomtchev/react-native-settings by mmomtchev 2 years ago
I’ve noticed that this error/warning happens when I use more than 1
await
within ait
statementI have had this problem come up in a variety of places and 99% of the time the problem was that I was waiting for synchronous code. That is why for some people the error went away, after they removed an
await
, it is a strong sign that the test only required oneawait
or that theawait
is in the wrong place. Something that works quite nicely in these situations is going through the test and remove allawait
s and then look at the code that is being tested to see where theasync
code is actually happening and to useawait
only once theasync
code is actually triggered. If the assertion still fails because the render did not finish have a look at the bottom examples, maybe they can help because they wait until something shows up or is finished running.The following three examples are the ways I tend to use await the most, maybe it helps clean up tests:
fireEvent
on the element. If you wait for an element to render with the below method and await the assertion as well, the error will probably come up. This is a classic case of unnecessary waiting that I describe at the top. Therefore, don’t wait for synchronous code.@KieranO547 though the code is quite simple two follow up question:
When you get that error is the code you posted the only code that is running? To make sure that is the case I would remove all other renderings and tests in the file and only run that one file. The weird thing about the error showing up for you is that the spec you posted seems to contain nothing that could trigger the error.
What does the code look like, which you are testing. Are there timeouts, state changes, animations etc. just the spec code is only one side of the medal and makes it very hard to debug.
@brunnohofmann @rmathias86 In my case, I found that the problem was caused by the following line:
Since version 6,
findBy
internally uses async/await and polls until it finds the element in question. I found that wrapping this in anexpect
(where this is the last line in my test), it creates a race condition between thefindBy
polling and the end of the test. SincefindBy
has a 4.5 second timer before it errors out, the “unmounted” error would appear 4.5 seconds after it is triggered and appear to be tied to a random test, even though it wasn’t actually caused by a random test.Replacing the offending line with this:
Fixed my problem.
I’ve found a way of hijacking the react-native jest preset, to avoid replacing the global Promise object with PromiseJs. Here is how it’s implemented
@mdjastrzebski now this could be a potential solution until https://github.com/facebook/react-native/issues/29303 is dealt with by the RN team. @testing-library/react-native could provide it’s own jest preset as a wrapper of the original react-native preset in a similar way than my example before. This preset can be optional as this issue does not affect everyone.
We used to have a preset and it was removed, so I’m sure there are some arguments against doing it again. However, this solves this issue and issues in #568 as well.
Any updates here? This is happening when i use more than 1
await
inside ait
Same issue here. I tried all the things I mentioned earlier, but none worked here.
I tried to do the suggested steps from @sbalay in this comment: https://github.com/callstack/react-native-testing-library/issues/379#issuecomment-714341282, but I was not able to make it run as expected in my setup.
What I did is something similar, I installed a Promise pollyfil with
npm install --save-dev promise-polyfill
. After that I added this into myjest.setup.ts
file (I guess it will work for.js
files too):Before the workaround:
After the workaround:
Looks like a good workaround for now.
@Norfeldt Here is your quick hack:
Create
test/asap.js
withThen add to
jest.config.js
:I guess this is the issue, too. In my case, those messages disappeared when I replaced
findBy
queries withgetBy
and left only onefindBy
query.Found it! Same issue as https://github.com/facebook/react-native/issues/29303 or https://github.com/facebook/jest/issues/10221 (and also interfering in https://github.com/callstack/react-native-testing-library/pull/568)
Not sure how and not sure why, but the fact that the react-native preset for jest overrides the Promise object is part of the cause behind this warning.
When I remove that line from the preset (in my node_modules folder) the warning disappears.
@milesj thanks for the clarification! Not happening in this case.
Maybe this will be helpful for someone. In my team we have a set of rules for writing tests with jest and testing library:
waitFor
in oneit
causes problems with jest promises - you can fix this by using Promise polyfillawait
on synchronous functions causes race conditions problems in testing-library. They occur randomly directly insideit
. When used insideact
orwaitFor
problem occurs more often - the only known solution for now is to avoid spammingawait
on every function invoked in testsfindBy*
insideexpect
leads to race conditions problems - instead of direct use, define variable before expect:fireEvent
insideact
may lead to problems with Promises and race conditions - use async act:await act(async () => fireEvent...)
findBy*
andwaitFor
in the sameit
can lead to react errorCan't perform a React state update on an unmounted component.
- for now the best known solution is to avoid this situations and instead offindBy
usewaitFor
together withqueryBy*
Warning: You called act(async () => ...) without await.
try to replacequeryBy*
andfindBy*
withgetBy*
. Check if allact
are used properly. Check if you don’t have any async methods without await.An update to [Component] inside a test was not wrapped in act(...)
even if allfireEvents
and other actions that need it are wrapped byact
. This can be caused by many reasons. I.e. in our project Apollos MockedProvider was not resolving promises on time (nor waiting for them). In rare cases you can try to use combination of async act and waitFor:They are not a 100% solution but we noticed that checking this list resolves most of the problems. In rest of the cases we just use trial and error method to find out what should be corrected 😦
i am having this problem with
@testing-library/react-native": "^11.5.0
Closing this issue, as it became non-actionable. Recent changes seem to have fixed the warnings for some users, while this issue contains various instances of
act
warnings, both sync and async, triggered by different conditions that probably deserve separate treatment.If you will find
act
warnings still occurring in the latest RNTL version, then please submit a GH issue with minimal repro repository based on oursexamples/basic
so that we have an actionable PR with clear repro case to fix.@jpmonette, @Norfeldt, @mmomtchev, @tcank, @rijk ^
This particular line looks a bit odd to me:
I don’t thing we support putting jest’s
expect
intowaitFor
.Could you try something like:
Let me know if you still got the same error.
In my case, it happens when using
await findByX
at least two times in the same test, so when callbacks do something asynchronously and I need to wait for an element to appear:I still have no idea why does it complain in such case, though…
I patched
react-test-renderer
(as a temporary fix) since this error was failing my CI tests.@mdjastrzebski , I ran reactnavigation example in this repo and still getting the above error. But when i downgrade to v6.0.0, it goes away. Can you please help me what’s going wrong ?
I also ran into same problem after upgrading to v7.0.1. Also, if you just run the react-navigation example in the repo itself you will see this problem.
I’m also getting this error when using
await
multiple times.Something I noticed though is that if I change the test from
async
/await
to normalthen
withdone
then the warning goes away.Test that gets a warning:
Without using
async
/await
:The component if it's useful
@jsamr You should wrap the
reload()
call inact
like this:As each state mutation should be wrapped in
act
. In case offireEvent
this is done for you.This leaves your code with following warning logged to console
which is simingly caused by
await findByText('load-cycle-1');
Be sure to only use
await findBy*
once and replace the rest of yourfindBy*
's withgetBy*
's. This solved the issue for me.For us, the issue seems to be due to the detection that calls to
act
are properly “awaited”.I patched that package and modified the detection to use
finally
instead of the double-then method. The end result was that most tests now pass without warnings now.Patch for react-test-renderer@17.0.2
before
after
Full patch for patch-package:
Edits:
A clear winner without the global promise hacks. #379 (comment)
Thank you kind sir! @babylone-star
@Norfeldt @milesj @mmomtchev do you use RNTL Jest preset:
preset: '@testing-library/react-native',
? Does using it make any difference regarding test runs?@janzenz you can do this using a non-async test and
then
:However, in most cases one
findBy
(orwaitFor
) is enough; once the first element is found the rest is usually there as well.Your approach solved the problem in my case. Thanks for sharing.
how came, expect is even used in the sample in the doc: https://testing-library.com/docs/dom-testing-library/api-async/#waitfor
I can confirm that removing line 24 from
node-modules/react-native/jest/setup.js
clears the warning.Not sure if there’s anything we can do besides patching it. I tried all proposed solutions and this was the only one that worked.
Having the same issue as @rmathias86
so what is the solution eventually?
After upgrading from 5.x to 7.x, I started to get randomly this following error. Sometimes all tests pass, sometimes not. This is really random.
I suspect this is related to the same problem.
@thymikee here is a repo with the example from @ryanhobsonsmith. It would be great if someone had a look.
Ok, Miles’ solution was too far off actually. In case anyone else finds this and wants a quick workaround inspired by: https://github.com/facebook/react/issues/15416, then the best hack I’ve got work around things is to just build in some manual wait time inside of an
act()
call like so:Would be happy to learn if anyone has worked out a better practice for testing these kinds of async event handlers / state-changes though.
@mdjastrzebski I ran into a similar issue (although, don’t know if it is exactly the same problem) and made a mcve here: https://github.com/jsamr/react-native-testing-library-379
This did fix this error for me. I am in a new codebase that I set up last week. I was on React Native Testing Library 11.1.0, and upgrading to 11.2.0 made it so the warning I was receiving went away. Here was the test where I had been getting the warning, though the test itself was passing. I presume I was getting the warning because I had multiple
await
s in it:React: 18.1.0 React Native: 0.70.1 RN Testing Library: 11.2.0 React Test Renderer: 18.1.0 (I don’t have this added explicitly in my project, included by RN Testing Library)
@Norfeldt:
if (argsWithFormat[0].includes("not wrapped in act(")) return
, no wonder there are no warnings 😉Having nothing better to do, I just wasted 3 hours on this shit, I know what is going on, but there is no easy solution.
This warning depends on a piece of remarkably kludgy (and fragile) code: https://github.com/facebook/react/blob/12adaffef7105e2714f82651ea51936c563fe15c/packages/react-dom/src/test-utils/ReactTestUtilsPublicAct.js#L129
(in React 18 this has been reshuffled around to another file but the problem remains)
Look at it:
in the function body and the return value is:
Normally the second useless
.then()
will be called after the.then()
of the return value if the function is awaited. However the return value is evaluated in the jest context - where aPromise
is Node.js’ internal promise. Thethen()
here is evaluated by the shim inreact-native/lib
- herePromise
is a JS constructor.Now enter asap - which is used by the
Promise
shim. It does what it says - it finds a way to flush allPromises
before anyone else has a chance to do something else. Which defeats that clever hack.@tcank that’s a very interesting trail. Great thanks for posting it here. I will try to dig deeper in the coming days/weeks.
For the case of multiples
await waitFor
in the same test case, arriving in “You called act(async () => …) without await” warning, we found a solution in this stack overflow post https://stackoverflow.com/questions/64952449/react-native-testing-act-without-await/69201830#69201830 There is more info about our case on a similar issue opened toreact-hooks-testing-library
: https://github.com/testing-library/react-hooks-testing-library/issues/825#issuecomment-1119588405I was having the same problem when I tried to use three findBy in a row. The error disappeared when replacing the last two calls with a getBy*.
Before
After
@rmathias86 I’m getting this error too, tests failing intermittently on CI. For us, the tests that fail are pretty random but none of them are using async/await or the findBy/waitFor API. What makes you think it’s related? Did you ever find a fix?
Btw, here’s the full stack trace, it’s worryingly small like most of it is missing
Great find!
There were 3 tests doing expect(findBy****). I extracted the findBy out of expect function, and put await before that. Solved my problem.
I tried replacing
promise
with the native Promise and it completely broke half of our tests. We even tried using native async/await instead ofregenerator-runtime
and it had the same problem. I’d wager a lot of RN APIs expect the timings provided bypromise
to work correctly, which is rather unfortunate.Nice find!
As mentioned, I think it’s because I do some fetching and react-query does some react state management. If I just had a way to render it and tell it when RQ is done with the state management, then that might solve it.
This basic example does not have reanimated.
Yes it does actually cause problems, with the biggest issue being that it masks failures and tests pass with a green status. This is primarily caused by RN overriding the global
Promise
.Once we patched that, it uncovered a massive amount of these problems. Invalid mocks, accessing on undefined errors, so on and so forth. I really have no idea why this happens, but it does.
Yes, it works like magic 🪄
This is a very big issue without any solution. Right now I’m “patching” it to go away 🙈, but have to use patch-package if this continues to be unsolved 🙊.
@rijk I have tried your solution for multiple findBy* by I’m getting this (rather nasty) error, not even sure what it means:
Hello all!,
I followed @MarceloPrado’s suggestion which is to comment out this line in react-native package: https://github.com/facebook/react-native/blob/master/jest/setup.js#L24. It does clear out the warnings however it breaks a few tests in our codebase and I couldn’t figure out on how to fix the tests.
I tried a different approach which is to update the waitFor function in the compiled js file here: https://github.com/callstack/react-native-testing-library/blob/master/src/waitFor.js#L57-L74 to be:
It seems to remove the warnings in our codebase (although we need to fix a couple of tests due to not handling the async operation properly). The thing that I’m unsure about is whether this will create another problems that I’m not aware of.
Having the same problem here!!!
@sbalay It’s possible to have duplicate transitive dependencies. Like in Yarn for example, you may have a lock file that has an entry like the following (Yarn sometimes doesn’t dedupe correctly).
I’m also getting this console error logged when I call findByText more than once. I’m trying to understand if this is an actual bug as @renanmav and @milesj seem to suggest or if I’m just not understanding something about how to use react testing library properly.
A simple (albeit contrived) example I’ve come up with to reproduce the bug is:
The test seems to work fine (passes as written, fails if I change expected values), but I still see the console.error message warning complaining that “You called act(async () => …) without await”
I get this console error once per additional time (beyond the first) that I make calls to
fireEvent.press(button)
andfindByText
in the same test. I’ve tried suggestions by @ccfz to wrap thefireEvent()
calls withawait act(async ...)
, but that didn’t help (not sure I understand how that would have helped either? Doesn’t fireEvent already wrap things inact()
?)Could someone help me understand what is going wrong here?
@ccfz Thanks for all the info. So to answer the 2 follow up Q’s,
react-native-elements
, which look like this:The touched state is only used for the disabled state of the login button which is exactly what I’m testing for.
Edit: I just refactored the test to be:
Matching your third suggestion and thats got rid of the messages for me so i suppose thats what i was missing, the error message handler is part of the component library - still getting used to testing lol - And thank you again!
I’ve dug into this a bit and I believe the issue is race conditions.
Here’s the act code and the block where the console log happens: https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiberWorkLoop.old.js#L3680 You’ll notice this just creates a resolved promise and logs in a then block (2 next ticks).
To ignore the error, the
then
of the initial render needs to fire here: https://github.com/facebook/react/blob/master/packages/react-reconciler/src/ReactFiberWorkLoop.old.js#L3701 However, in my testing, this is actually firing after the resolved promise above, resulting in the warnings. Why? Because RN/React rendering is slow? Not really sure, but there’s definitely not an easy way to fix it, or no way in general.I suggest use wait-for-expect directly.
I’m facing the same error but my test is different. Before moving from version 2 to 7 my test was the same and I have no errors, but after updating to v7 i’ve been getting the same error.
I have something really simple like this:
I use
act
because when I fire the event I have some async logic in my component that will re-render it. If i don’t use the last code I’ve got the next error:UPDATE: 11-08-2020 I changed (cleaned) my tests. I don’t have to use anymore this awful thing I was doing adding await to all the
act
functions I was using and It worked fine. BUT, this is not always for all the cases, so you must be careful how is written your async code and then how is written your test… testing this async code. Also, you must be careful if you are using async callings inside theuseEffect
hook. I was concerned about this but I was doing it wrong in some parts. I’ll share with you what I’m doing with my code now, which thanks to the tests and the library, I think is more solid:I’m adding a variable to know if you component is mounted/unmounted. With this variable validate wether update or not the state of the componente, which cause to re-render the component.
My test now it is like this:
Hope this might help 😃
One last thing! Don’t forget to use the
done
function 😋@jsamr thanks for posting repo, I’ll try to reproduce that tomorrow.
@mdjastrzebski I did update my code and still get the issue. Here’s the updated test: