Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React testing-library - Testing a promise that set states in the first useEffect hook

I have a useEffect hook that loads when the component is mounted, as so:

useEffect(() => {
   listFiles().then(keys => {
      setKeys(keys)
      console.log(keys)
   }).catch(err => {
        showFail(err.message)
   })
}, [])

I'm trying to test the function with react testing-library, simply using the render function:

beforeEach(() => {
  render(<Dashboard />)
})

However, when I run any test that resolves the promise and sets the state:

jest.mock('../utils/storage', () => ({
    listFiles: jest.fn(() => Promise.resolve([])),
}))

I end up with a weird warning message about using act to to wrap the event:

  Warning: An update to Dashboard 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 .. 
        in Dashboard

      18 |     useEffect(() => {
      19 |         listFiles().then(keys => {
    > 20 |             setKeys(keys)
         |             ^
      21 |             console.log(keys)
      22 |         }).catch(err => {
      23 |             showFail(err.message)

I have tried wrapping the render in an act, but it doesn't seem to change anything.

Any suggestions as to what I'm doing wrong here? Should I be rendering in some other way?

Thanks in advance!

like image 663
Sam Avatar asked Oct 16 '22 04:10

Sam


1 Answers

This error usually happens when you try to assert before the component has finished updating all state.

Note that inside listFiles you are calling setKeys(keys) and that updates the state.

You should await for the new keys(or files) to show up in the document:

expect(await findByText('some file name or key')).toBeInTheDocument();
// more asserts here

Alternatively, you can waitFor the mock to have been called (although I think the option above is better).

await waitFor(() => expect(listFilesMock).toHaveBeenCalled());
// more asserts here

The methods above should already be wrapped in act for you by the React Testing Library, so you don't need to do it


What is act() from the React docs:

When writing UI tests, tasks like rendering, user events, or data fetching can be considered as “units” of interaction with a user interface. React provides a helper called act() that makes sure all updates related to these “units” have been processed and applied to the DOM before you make any assertions.

The name act comes from the Arrange-Act-Assert pattern.


Useful links:

  • Fix act warning by React Testing Library author
  • React Act docs
like image 54
Doug Avatar answered Oct 23 '22 09:10

Doug