Reputation: 7768
This minimum working example demonstrates what happens in a smaller test scenario:
src/code.test.js
:
import {useState, useEffect, useCallback} from 'react';
import {render, waitForElementToBeRemoved, fireEvent} from '@testing-library/react';
global.fetch=url=>Promise.resolve({json: ()=>{
return Promise.resolve('test');
}});
const Component = ({callback}) => {
const [serverData, setServerData] = useState(null);
useEffect(()=>{
fetch('/api/get_it').then(r=>r.json()).then(data=>{
setServerData(data);
});
}, []);
if (!callback) {
return <div>No Callback</div>;
}
return <div>Server data: {serverData}</div>;
}
test('without callback', async () => {
const component = render(<Component/>);
component.getByText('No Callback');
});
package.json
:
{
"dependencies": {
"@testing-library/react": "^13.3.0",
"react-scripts": "5.0.1"
},
"scripts": {
"test": "react-scripts test --verbose",
"check": "jest --version"
}
}
When running this (npm i && npm test;
), I get
Warning: An update to Component inside a test was not wrapped in act(...).
...
7 |
8 | const Component = ({callback}) => {
> 9 | const [serverData, setServerData] = useState(null);
| ^
10 |
This is because of the setServerData
call after the fetch
on line 12 is resolved. In this test case, with the missing callback
, I don't care about the serverData
. The naive approach to fixing this would be to add callback
to the dependency array for the useEffect
on line 15. That is not acceptable because that useEffect
should not run again any time callback
changes!
How to solve the "update was not wrapped in act()" warning in testing-library-react? is materially different from that question because in this question, in my code there is nothing to find.
How can I write this test to avoid this warning?
Upvotes: 2
Views: 1722
Reputation: 7768
Make sure all state changes are done before proceeding:
let component;
await act(()=>{
component = render(<Component/>);
});
component.getByText('No Callback');
(Import act
from the testing library.)
Now, no matter how many inner fetches, useEffect
s or useEffect
s that trigger other useEffect
s happen, they (and the state changes they cause) will all be done by the time the getByText
runs and the test ends.
Upvotes: 1
Reputation: 1297
Your fetch
call runs even if callback
is undefined which triggers a state change. In most cases you would use act
and/or waitFor
to test if your state change has been completed but in this case those are not required because your DOM doesn't change.
A simple fix would be to check if callback
is defined in the functioned passed to useEffect. You only need to update state if callback is defined.
if(!!callback){ //if callback is defined then fetch
fetch('/api/get_it').then(r=>r.json()).then(data=>{
setServerData(data);
});
}
Upvotes: 0