apollo-client: MockedProvider causing test to complain about not using `act()`.
Intended outcome:
Actual outcome:
When running test using MockProvider I expect test to not error out with the following error:
Warning: An update to ResearchCategoryList inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
How to reproduce the issue:
The following test will cause test to complain:
it('renders without error', async () => {
let component
act(() => {
component = create(
<MockedProvider mocks={mocks} addTypename={false}>
<ResearchCategoryList category="Education" />
</MockedProvider>
)
})
await wait(0)
const tree = component.toJSON()
expect(tree).toMatchSnapshot()
Versions
"@apollo/react-testing": "^3.1.3",
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 30
- Comments: 26 (2 by maintainers)
@donedgardo I’ve beeing having the same issue, using Jest and @testing-library/react. Right now I’m suppressing those warnings, as described in the in the @testing-library/react documentation.
In my jest global setup file, I’ve added
I’m not completely sure about this approach (this might suppress warnings that need to be taken care of), but that does the trick! 😄
@donedgardo Thanks for pointing that issue out. I’ve been using the
wait
exported from@testing-library/react
and that fixes the issue.In your case, I think you can try and wrap the
await wait()
inact
as suggested in this comment.Both solutions work for me! 🎉
@DylanRJohnston that is exactly the point. It is a hack but it stops log spam in my tests. When I just have
await new Promise((resolve) => setTimeout(resolve, 0))
every test that is testing final state receivesWarning: An update to MyComponent inside a test was not wrapped in act(...).
in the logs.From my understanding we are waiting for apollo client to update its internal state from loading to the mocked final state. By using the
waitFor
we are getting a nicer to read version of wrapping it inact
. The internal promise is just there to ensure the event log ticks over to the final state.Another alternative that works is
await act(() => new Promise((resolve) => setTimeout(resolve, 0)));
Edit: While I think of it it would be nice if the API had a
renderFinalState
orwaitForFinalState
function included.For anyone else that comes across this I used the following
Hi @Frozen-byte, I suggest you use the
render
from@testing-library/react
.Does that help you?
Found the solution!
The problem is
act
must scope all changes to hooks, this includes the initial render, and any event handlers that trigger Apollo. So you need thewait
after the render, but still inside a call to act.It might be a bit of an overkill, but I hate waiting a random number of seconds so I just hooked some deferred promises on the mocks so I can wait for them to finish. I did this mostly because in our project we are using multiple apollo providers so we had random failing tests due to CPU differences between local and CI servers.
I hope this helps, and hopefully the apollo team will include something like this in a future release using the internal
renderPromises
.helpers/tests/mockedProvider.jsx
component.test.jsx
LATER EDIT: I created a gist where I also removed some useless code. You don’t need to wait for the loading state to test it and data and error are final states ( or at least in unit testing ).
With
wait()
testing-library will complain thatwait
is depreacted. My solution:There is a lot of history in this issue, and the problems listed head in all sorts of different directions. If anyone thinks this is still a problem in
@apollo/client@latest
, and can provide a small runnable reproduction, we’ll take a closer look. Thanks!still having that same issue… problem now is, sometimes the tests work without a problem, but sometimes they don’t and they will throw that
act
error…I just created a wrapper of render to handle this
Then you can just
Be sure to also stick a
wait
in any events that also trigger responses from apollo, e.g.I feel as though this may be caused by https://github.com/apollographql/apollo-client/issues/5869
Here’s the code I had to write to alleviate the problem, and the test case shows the problem. Essentially, you need to
await
to get another tick of the event loop because Apollo under the hood is causing multiple returns fromuseQuery
.@hwillson I used the example from your docs. Go here and run
yarn test
to see the problem: https://replit.com/@redreceipt/MelodicKosherSubversion@ryan-rushton, that’s a misuse of
waitFor
.waitFor
repeatedly calls the function it’s given until it stops rejecting the promise / throwing an error, e.g. usingfindBy
to wait for a component to appear in the dom. Since your function never rejects thewaitFor
does nothing and is equivalent toawait new Promise((resolve) => setTimeout(resolve, 0))
If i use
waitfor()
, i can’t test the loading state.how can i test loading state?
Yes that helped a lot! Here is my not simplified working code, without enzyme:
// edit: as I tried to split the loading and content part into two assertions I noticed that it is obligatory to
await wait()
the test (even if it’s the last line), otherwise jest will complain about missing act.