Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to solve the "update was not wrapped in act()" warning in testing-library-react?

I'm working with a simple component that does a side effect. My test passes, but I'm getting the warning Warning: An update to Hello inside a test was not wrapped in act(...)..

I'm also don't know if waitForElement is the best way to write this test.

My component

export default function Hello() {   const [posts, setPosts] = useState([]);    useEffect(() => {     const fetchData = async () => {       const response = await axios.get('https://jsonplaceholder.typicode.com/posts');       setPosts(response.data);     }      fetchData();   }, []);    return (     <div>       <ul>         {           posts.map(             post => <li key={post.id}>{post.title}</li>           )         }       </ul>     </div>   ) }  

My component test

import React from 'react'; import {render, cleanup, act } from '@testing-library/react'; import mockAxios from 'axios'; import Hello from '.';  afterEach(cleanup);  it('renders hello correctly', async () => {   mockAxios.get.mockResolvedValue({     data: [         { id: 1, title: 'post one' },         { id: 2, title: 'post two' },       ],   });    const { asFragment } = await waitForElement(() => render(<Hello />));    expect(asFragment()).toMatchSnapshot(); }); 
like image 386
Pablo Darde Avatar asked Feb 07 '20 14:02

Pablo Darde


People also ask

When testing code that causes React State updates should be wrapped into act RTL?

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.

Should be wrapped in Act?

You NEVER need to wrap userEvent or fireEvent in act. They are *already* wrapped in act. Some have noticed that they see an "act warning" go away when wrapping these in an async act. The warning would go away just as well if you added: `await act(async () => {})` on the next line.

What is act in React testing library?

react-dom/test-utils 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: act(() => { // render components }); // make assertions.

What does cleanup do in React testing library?

cleanup ​ Unmounts React trees that were mounted with render. Please note that this is done automatically if the testing framework you're using supports the afterEach global and it is injected to your testing environment (like mocha, Jest, and Jasmine).


1 Answers

Updated answer:

Please refer to @mikaelrs comment below.

No need for the waitFor or waitForElement. You can just use findBy* selectors which return a promise that can be awaited. e.g await findByTestId('list');


Deprecated answer:

Use waitForElement is a correct way, from the docs:

Wait until the mocked get request promise resolves and the component calls setState and re-renders. waitForElement waits until the callback doesn't throw an error

Here is the working example for your case:

index.jsx:

import React, { useState, useEffect } from 'react'; import axios from 'axios';  export default function Hello() {   const [posts, setPosts] = useState([]);    useEffect(() => {     const fetchData = async () => {       const response = await axios.get('https://jsonplaceholder.typicode.com/posts');       setPosts(response.data);     };      fetchData();   }, []);    return (     <div>       <ul data-testid="list">         {posts.map((post) => (           <li key={post.id}>{post.title}</li>         ))}       </ul>     </div>   ); } 

index.test.jsx:

import React from 'react'; import { render, cleanup, waitForElement } from '@testing-library/react'; import axios from 'axios'; import Hello from '.';  jest.mock('axios');  afterEach(cleanup);  it('renders hello correctly', async () => {   axios.get.mockResolvedValue({     data: [       { id: 1, title: 'post one' },       { id: 2, title: 'post two' },     ],   });   const { getByTestId, asFragment } = render(<Hello />);    const listNode = await waitForElement(() => getByTestId('list'));   expect(listNode.children).toHaveLength(2);   expect(asFragment()).toMatchSnapshot(); }); 

Unit test results with 100% coverage:

 PASS  stackoverflow/60115885/index.test.jsx   ✓ renders hello correctly (49ms)  -----------|---------|----------|---------|---------|------------------- File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s  -----------|---------|----------|---------|---------|------------------- All files  |     100 |      100 |     100 |     100 |                     index.jsx |     100 |      100 |     100 |     100 |                    -----------|---------|----------|---------|---------|------------------- Test Suites: 1 passed, 1 total Tests:       1 passed, 1 total Snapshots:   1 passed, 1 total Time:        4.98s 

index.test.jsx.snapshot:

// Jest Snapshot v1  exports[`renders hello correctly 1`] = ` <DocumentFragment>   <div>     <ul       data-testid="list"     >       <li>         post one       </li>       <li>         post two       </li>     </ul>   </div> </DocumentFragment> `; 

source code: https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/60115885

like image 57
slideshowp2 Avatar answered Sep 23 '22 02:09

slideshowp2