I'm trying to figure out if there is a way to prevent the "not wrapped in act(...)" warning thrown by Jest/testing-library when I have nothing to assert after the state update that causes the warning happens, or if I should just ignore this warning.
Suppose I have this simple component:
import React, {useEffect, useState} from 'react';
import {getData} from 'services';
const MyComponent = () => {
const [arr, setArr] = useState([]);
useEffect(() => {
(async () => {
const {items} = await getData();
setArr(items);
})();
}, []);
return (
<div>
{!(arr.length > 0) && <p>no array items</p>}
{arr.length > 0 && (
<ul>
{arr.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
)}
</div>
);
};
export default MyComponent;
Suppose I want to simply test that this component renders okay even if getData()
doesn't return any data for me.
So I have a test like this:
import React from 'react';
import {getData} from 'services';
import {render, screen} from 'testUtils';
import MyComponent from './MyComponent';
jest.mock('services', () => ({
getData: jest.fn(),
}));
it('renders', () => {
getData.mockResolvedValue({items: []});
render(<MyComponent />);
expect(screen.getByText('no array items')).toBeInTheDocument();
});
This test will pass, but I'll get the "not wrapped in act(...)" warning because the test will finish before getData()
has a chance to finish.
In this case, the response from getData()
sets arr
to the same value (an empty array) as I have initially set it to at the top of the component. As such, my UI doesn't change after the async function completes—I'm still just looking at a paragraph that says "no array items"—so I don't really have anything I can assert that would wait for the state update to complete.
I can expect(getData).toHaveBeenCalledTimes(1)
, but that doesn't wait for the state to actually be updated after the function call.
I have attempted an arbitrary pause in the test to allow time for setArr(items)
to happen:
it('renders', async () => {
getData.mockResolvedValue({items: []});
render(<MyComponent />);
expect(screen.getByText('no array items')).toBeInTheDocument();
await new Promise(resolve => setTimeout(resolve, 2000));
expect(screen.getByText('no array items')).toBeInTheDocument();
});
But that doesn't seem to help, and I'm honestly not sure why.
Is there a way to handle this situation by modifying only the test?
I am sure I could fix the problem by refactoring MyComponent, e.g., by passing arr
to MyComponent as a prop and moving the getData()
call to a parent component, or creating some custom prop used only for testing that would skip the getData()
call altogether, but I don't want to be modifying components purely to avoid warnings in tests.
I am using testing-library/react, v11.2.2.