When trying to test the component which dispatches async thunk I get the following warnings. They are displayed because of updates performed after the test is finished.
console.error
Warning: An update to App 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://reactjs.org/link/wrap-tests-with-act
at App (/home/karlosos/Dev/nokia/playground/testing-redux/src/App.tsx:6:34)
at Provider (/home/karlosos/Dev/nokia/playground/testing-redux/node_modules/react-redux/lib/components/Provider.js:19:3)
at Wrapper (/home/karlosos/Dev/nokia/playground/testing-redux/src/testUtils.tsx:11:22)
at printWarning (node_modules/react-dom/cjs/react-dom.development.js:86:30)
at error (node_modules/react-dom/cjs/react-dom.development.js:60:7)
at warnIfUpdatesNotWrappedWithActDEV (node_modules/react-dom/cjs/react-dom.development.js:27589:9)
at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:25508:5)
at forceStoreRerender (node_modules/react-dom/cjs/react-dom.development.js:16977:5)
at Object.handleStoreChange [as callback] (node_modules/react-dom/cjs/react-dom.development.js:16953:7)
at node_modules/react-redux/lib/utils/Subscription.js:23:20
This is explanation why warnings are visible.
One way of fixing them is to introduce some kind of barrier that will wait for all pending async actions to be finished. But doing that my tests would need to have asserts for logic that I don't want to test.
I have recreated a minimal reproducible project here: https://github.com/karlosos/react-redux-async-warnings/tree/main/src
My example test looks like this:
test('WHEN component rendered THEN counter value is being loaded', () => {
// WHEN
renderWithProviders(<App />)
// THEN
expect(Api.getValue).toHaveBeenCalledTimes(1);
const loadingSpinner = screen.getByTestId('loading-spinner');
expect(loadingSpinner).toBeInTheDocument();
// things will happen to the component here after test is done
// precisely the data fetched from API will be displayed
});
and example thunk:
export const fetchCounterValue = (): AppThunk => async (dispatch, getState) => {
if (getState().counter.fetchValueStatus === "loading") {
return;
}
dispatch(fetchValueStart());
try {
const result = await Api.getValue();
dispatch(fetchValueSuccess(result));
} catch (e) {
dispatch(fetchValueError('Could not fetch the data'));
}
};
waitFor
at the end of the testWhen I've added await waitFor(() => new Promise(res => setTimeout(res, 0)));
at the end of the test then warnings are not visible. But I don't want to edit every single test case. It seems like a hack.
test('WHEN component rendered THEN counter value is being loaded', async () => {
// WHEN
renderWithProviders(<App />)
// THEN
expect(Api.getValue).toHaveBeenCalledTimes(1);
const loadingSpinner = screen.getByTestId('loading-spinner');
expect(loadingSpinner).toBeInTheDocument();
await waitFor(() => new Promise(res => setTimeout(res, 0)));
});
The correct solution is not to set global.IS_REACT_ACT_ENVIRONMENT = false;
, but to make the correct test case in the first place. This act(...
) warning means that there are some updates happening to the component after the test has finished.
In this particular scenario, it is caused by the loading spinner disappearing and then data being displayed. To fix this test scenario, we need to wait for this loading spinner to disappear or alternatively wait for data to appear on the screen.
This is what a correct test case should look like:
test('WHEN component rendered THEN counter value is being loaded', async () => {
// WHEN
renderWithProviders(<App />)
// THEN
expect(Api.getValue).toHaveBeenCalledTimes(1);
const loadingSpinner = screen.getByTestId('loading-spinner');
expect(loadingSpinner).toBeInTheDocument();
await waitForElementToBeRemoved(() => screen.queryByTestId('loading-spinner')); // This is preventing `act(...)` warnings
});
Learn more about act(...)
warnings here: