I am trying to write a unit test that will test a useEffect that calls an api (let’s call it fetchData
) so that when the component containing the useEffect unmounts or re-renders from the dependencies updating, we can test the cancellation path.
Here’s a mockup version of my code here:
export function useCustomHook(props: { foo: string }) {
const [state, setState] = useState<any>();
useEffect(() => {
let isCancelled = false;
fetchData()
.then((response) => {
if (isCancelled) {
console.error('Fetch api request cancelled');
return;
}
setState(response);
});
.error( // like the .then(), setState(error) if not cancelled only )
return () => { isCancelled = true; }
}, [foo]);
return state;
}
i’m trying to make a test with the @testing-library/react library so that we test the setState wasn’t called if the dependency foo
gets updated before fetchData
finishes.
To ensure I get this, I mocked fetchData so that it uses setTimeout for 5 seconds and then returns a successful response per normal.
This is my jest test so far:
it('should not update state if cancelled' , () => {
// assume it's properly mocked
const mockFetchData = jest.fn().mockImplementation(async () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(mockData);
}, 5000); // simulate a long request so we can cancel it
});
});
const { rerender } = renderHook(() =>
useCustomHook({ foo: 'bar' });
});
rerender({ foo: 'baz' });
expect(console.error).toHaveBeenCalled(); // this line fails
});
What am I missing here? The console.error never executes after the rerender and i put temporary console.logs everywhere to verify this (the last thing that executes is the effect’s cleanup function). Is there a way to test this condition?