-
-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve docs to avoid 'act' warning when using react-query in tests #432
Comments
Here's a test that waits on react-query to load the data, forgot to include that when I filed the issue. test('fetches data on load', async () => {
await act(async () => {
const { getByTestId } = render(<Component />);
const loadingStatus = getByTestId('testdata');
await wait(() =>
expect(loadingStatus).toHaveTextContent('{"content":"pong"}')
);
cleanup();
});
}); |
We don't see this issue in our own tests, so I'm not really sure what's going on with all of this. Are you using react-testing-library? |
Yeah. Let me see if I can put up a codesandbox for this...
…On Fri, May 1, 2020 at 10:52, Tanner Linsley ***@***.***> wrote:
We don't see this issue in our own tests, so I'm not really sure what's
going on with all of this. Are you using react-testing-library?
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#432 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAHGCFXZPMGMMQ4LFXB53DTRPMD4TANCNFSM4MXGNR7Q>
.
|
Alright here's the codesandbox: https://codesandbox.io/s/jovial-sanne-mhgb8?file=/src/app.test.js You may have to run the tests a few times to get the warning to show up (screenshot) |
Interesting. I have no idea why this should be required. |
The state is trying to update due to the hook, but the component / test harness has already torn down which throws the warning. You could also |
The query has already been completed; the codesandbox demonstrates that case. The problem seems to be isolated to the refetch timer not getting cleaned up before the test exits the ‘act’ scope. The cleanup call is still being made by react testing library, just outside of the act. Order of events:
Without manually calling cleanup I don’t see a way to prevent the state update from getting scheduled. |
I'm seeing this and it looks like I then reloaded the same test until I got the act warning because I'm not sure what the right thing to do is, perhaps the test harness I'm also not sure if there should be two different configs for |
I've definitely observed this same behavior in https://github.com/kentcdodds/bookshelf. I haven't had time to debug, but it definitely is annoying. @dustinsoftware's analysis is consistent with what I assumed is happening. And I agree with @ilovett that we should use the same config for tests and production. With that in mind, I'm not 100% certain of the best way to approach this. It's really hard to reproduce in a debugging environment. Right now, the manual cleanup at the end of the test is the best workaround I can think of. Really wish that the afterEach hook was called synchronously after each test 😐 |
@kentcdodds I've also come across this issue too. It gets tricky to debug exactly which test is causing the the I'm going to make a ticket for this in the RTL repo, but I think this highlights a potential need to improve the error message to display exactly which test is causing the issue. I'm curious whether it's possible to log out the first argument of the |
Something interesting that seems to have worked for me is doing this: render(<ComponentThatUsesReactQuery />)
await waitFor(() => {
if (queryCache.isFetching) {
throw new Error('The react-query queryCache is still fetching')
}
}) If I stick that in my app's custom |
The trick here is that it doesn't allow you to test the loading state, so for those you'll probably want to make your assertions between the regular render call and the Still trying to figure out the best way to do this, but that's one idea. |
Okay, with the latest release, can we confirm that as long as queryCache.clear is called after the test that all is well? |
@tannerlinsley With the latest release, I can't reproduce the issue even without calling So for me, the problem appears to be resolved. |
I'm definitely still experiencing this without calling |
Unfortunately it's intermittent. Sounds like a race condition. If I'm able to figure out exactly what the problem is then I'll raise this issue again. |
@kentcdodds You're right, it is intermittent now that I've been using the new version more. It's definitely better than the previous version. @tannerlinsley @kentcdodds If there's something you want me to try with either library, let me know and I'd be more than happy to help. |
I thought that this may be an issue with the let started = []
let testEnded = false
function start() {
const interval = setInterval(() => {
if (testEnded) {
console.log('interval ran after test ended')
}
}, 0)
started.push(interval)
}
afterEach(async () => {
for (const interval of started) {
clearInterval(interval)
}
testEnded = false
})
test('start 1', async () => {
start()
await new Promise(resolve => setTimeout(resolve, 10))
testEnded = true
})
test('start 2', async () => {
start()
await new Promise(resolve => setTimeout(resolve, 10))
testEnded = true
}) I assumed that I would see some logs, but in my testing, I do not. This means something else fishy is going on. Still digging. |
Ah! I figured it out. The reason this is happening is because the cleanup process actually includes flushing existing effects/etc. which involves waiting for the next tick of the event loop (via let started = []
let testEnded = false
function start() {
const interval = setInterval(() => {
if (testEnded) {
console.log('interval ran after test ended')
}
}, 0)
started.push(interval)
}
afterEach(async () => {
await new Promise(resolve => setImmediate(resolve))
for (const interval of started) {
clearInterval(interval)
}
testEnded = false
})
test('start 1', async () => {
start()
await new Promise(resolve => setTimeout(resolve, 10))
testEnded = true
})
test('start 2', async () => {
start()
await new Promise(resolve => setTimeout(resolve, 10))
testEnded = true
}) And that does result in getting the logs. Looks like there was already a PR to React Testing Library to address this issue: testing-library/react-testing-library#632 I didn't really understand why it was necessary and ultimately @kamranayub created #332 to fix the underlying issue. Come to think of it... It's been a while since I've seen act warnings so this might actually be fixed and I just confused myself (and used up a lot of time on this one 😅). But, if anyone experiences act warnings from Leaving this as-is right now though. |
I am still facing this issue, can someone help and tell me what the fix is? |
I'm working through this right now. The best solution to this I've found (but is not perfect) is to make sure you do |
Thank you for the quick reply @tannerlinsley, just so I am clear does it mean I have to use a custom renderer to have access to |
For anyone else looking for how to actually do this, all you have to do is import queryCache and call the clear method on it. No need for custom renderer. import { queryCache } from "react-query";`
afterEach(() => {
queryCache.clear();
}); |
@iamtekeste your trick worked for us with import { queryCache } from "react-query";`
afterEach(() => {
queryCache.clear({ notify: false });
}); Otherwise, each test was failing with a timeout:
|
@kachkaev thank you for sharing your solution, it will come in handy when I upgrade to v2. |
I'm facing an issue which I suppose is related to this. I wanted to leave a comment here to aid those googling for it (I've stumbled upon this thread by sheer luck). My problem:After my tests finish running, I get an annoying
message from Jest. Moreover, in our CI/CD environment, the tests don't finish running until the timer for the cache garbage collection is executed (which delays our deploys by 5 min.. not terrible, but definitely annoying). SolutionTo avoid having to write
to my Thanks a lot to all of you who suggested using |
I keep getting the warning unless I use both I wrote a small utility function that wraps a render function with both: export function getRenderWithQueryCache<TRenderArgs, TResult = RenderResult>(renderFunction: (args?: TRenderArgs) => TResult): (args?: TRenderArgs) => Promise<TResult> {
afterEach(() => {
queryCache.clear({ notify: false });
});
return async function renderWithQueryCache(...args) {
const renderResult: TResult = renderFunction(...args);
await waitFor(() => !queryCache.isFetching);
return renderResult;
}
} |
This is what we're doing currently. You may want to include import { act, waitFor } from '@testing-library/react';
import { queryCaches } from 'react-query';
afterEach(async () => {
act(() => {
// Clear all query caches (including the default)
queryCaches.forEach(cache => cache.clear({ notify: false }));
});
// Hack to wait for timers to avoid act warnings
await waitFor(() => {});
}); We use the |
This did the trick for me when testing react hooks: (thanks @kentcdodds #432 (comment))
|
Hi @liorfrenkel , we are experiencing tones of warning, I am looking at your trick and I can't tell what is |
|
Thanks for sharing. I still experience a tons of This is my logs: console.error
Warning: An update to ForgottenPassword 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://fb.me/react-wrap-tests-with-act
in ForgottenPassword
in QueryClientProvider
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 warnIfNotCurrentlyActingUpdatesInDEV (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13729:7)
at dispatchAction (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6405:9)
at node_modules/react-query/lib/react/useBaseQuery.js:48:9
at node_modules/react-query/lib/core/queryObserver.js:436:13
at notifyFn (node_modules/react-query/lib/core/notifyManager.js:13:3)
console.error
Warning: An update to ForgottenPassword 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://fb.me/react-wrap-tests-with-act
in ForgottenPassword
in QueryClientProvider
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 warnIfNotCurrentlyActingUpdatesInDEV (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:13729:7)
at dispatchAction (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6405:9)
at node_modules/react-query/lib/react/useBaseQuery.js:48:9
at node_modules/react-query/lib/core/queryObserver.js:436:13
at notifyFn (node_modules/react-query/lib/core/notifyManager.js:13:3) We are experiencing tons of those error and we would gladly want to know how to remove or at least disable them, Can anyone tell a fix that work in the latest react-query version? |
@kopax it seems that your test is finished with that line 30, you will need to wait for the action that is hooked to the button click in your test and then also, you do not test a hook but a component, the hack that you are using is for testing hooks, try this one for he normal
|
Yes the action is react-query (all our act errors comes out from react-query usage), it exactly fails when the code execute We already added in We use the <ButtonPrimary
title={_(t`Valider`)}
onPress={() => refetch()}
disabled={shouldDisableValidateButton || isFetching}
/> I thought I could wait until this switch to true or false but the props are dispatched differently in the final components and @tannerlinsley as this is still a recurrent issue in projects that use |
@kopax I believe the problem is in the test and not in react-query, basically the act warning that you get is right, something is happening after the test is finished and there is no check for it in the test, check this out act warning try doing something like this:
|
Thanks for trying to help, I already tried to get the button state change and couldn't find a method to test the passed props (such as If you clone the code base and run |
@kopax i was able to do a useMutation hook test like this, @liorfrenkel suggestion worked for me
|
This is sort of related to #270. Filing in case we can improve the docs to help others avoid this issue.
When running tests, I ran into this error:
Here's the test:
And here's the component (simplified):
In #271 there was some cleanup logic added, however the error in my case appears to have been thrown before the cleanup call was fired.
I can fix the issue by adding to my tests at the end of each act call:
Doing the cleanup outside the act does not help; that appears to be too late.
Is it necessary to add the
cleanup
call inside everyact
call? If so, can we update the react-query docs on how to test with jest + react-testing-library? I bet others will run into this issue as well and hoping to save some time as more users adopt the library.Here's some screenshots with a debugger attached indicating the warning is originally caused by a call to
refetch
:Versions:
The text was updated successfully, but these errors were encountered: