reactjsjestjsreact-testing-libraryreact-testing

React Testing Library -- Ignore Unneeded Side-Effects ("Not Wrapped in Act" Warning)


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?


Solution

  • 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, useEffects or useEffects that trigger other useEffects happen, they (and the state changes they cause) will all be done by the time the getByText runs and the test ends.