lmat - Reinstate Monica
lmat - Reinstate Monica

Reputation: 7768

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?

Upvotes: 2

Views: 1722

Answers (2)

lmat - Reinstate Monica
lmat - Reinstate Monica

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, 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.

Upvotes: 1

W-B
W-B

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

Related Questions