Solving the console.error Warning: An update to App inside a test was not wrapped in act(…).
Solving the console.error Warning: An update to App inside a test was not wrapped in act(…).

There is multiple ways to setup a Custom Render that has already all your providers. Here you are going to see some sintax honey that will allows you to render your components using the React Testing Library, passing as options the providers you want to activate around that Component.

In this similar way:

renderUi(<Component />, { withReduxProvider: true, withReactQueryProvider: true, })

In this example we use some Providers that you might not use on your application, also you might use some other I’m not using here. Just addapt your code following this same pattern. The same goes for versions, this code might change on the basis of how this Libraries change their implementation.

Let’s continue.

First create our helper file in where we are going to make this possible:

Table of Contents

Implementation

// this file simplifies the component setup process when rendering the component for testing import React from 'react'; import { render, RenderOptions } from '@testing-library/react'; import { Provider } from 'react-redux'; import { MemoryRouter as Router } from 'react-router-dom'; import { QueryClientProvider } from 'react-query'; import thunk from 'redux-thunk'; import configureStore from 'redux-mock-store'; import axios from 'axios'; import store from '/services/rootReducer'; import { createTestQueryClient } from '/services/test/utils'; interface IExtendedRenderOptions extends RenderOptions { withRouter?: boolean routerHistory?: string[] withRedux?: boolean mockInitialState?: any withQueryProvider?: boolean mockAxiosCalls?: any } // wrappers and contexts const wrapInRedux = (componentTree: JSX.Element, { mockInitialState }: IExtendedRenderOptions) => { const storeConfig = configureStore([thunk]); const storeMock = mockInitialState ? storeConfig(mockInitialState) : store; // This allows you to use a passed mock store return ( <Provider store={storeMock}> {componentTree} </Provider> ); }; const wrapInRouter = (componentTree: JSX.Element, routerHistory?: string[]) => ( <Router initialEntries={routerHistory}> {componentTree} </Router> ); const wrapInQueryProvider = (componentTree: JSX.Element) => { const testQueryClient = createTestQueryClient(); return ( <QueryClientProvider client={testQueryClient}> {componentTree} </QueryClientProvider> ); }; // You don't need this option if you are using something more advance such as Mock Service Worker // This Function Mock axios calls and hand back a response for each call finding the right response by comparing the called endpoint with the endpoint fragment you passed // You need to pass an array of objects, each object will represent a call // { endpointFragment: string, expectedMockResponse: yourMockType } type AxiosCallMock = { endpointFragment: string, expectedMockResponse: any } // This one will runs on each axios get request const mockAxiosCallResponsesIfAny = (renderOptions?: IExtendedRenderOptions) => { if (renderOptions?.mockAxiosCalls) { axios.get.mockImplementation((url) => { // If your component has multiple axios calls make sure to pass // endpoint fragments that identify them const optionObject = renderOptions?.mockAxiosCalls.find((mock: AxiosCallMock) => url.includes(mock.endpointFragment)); return Promise.resolve(optionObject.expectedMockResponse); }); } }; const setupComponent = (ui: JSX.Element, renderOptions?: IExtendedRenderOptions) => { if (!renderOptions) return ui; let componentTree = <>{ui}</>; if (renderOptions.withRouter) componentTree = wrapInRouter(componentTree, renderOptions.routerHistory); if (renderOptions.withRedux) componentTree = wrapInRedux(componentTree, renderOptions); if (renderOptions.withQueryProvider) componentTree = wrapInQueryProvider(componentTree); return componentTree; }; const customRender = (ui: JSX.Element, renderOptions?: IExtendedRenderOptions) => { try { mockAxiosCallResponsesIfAny(renderOptions); const componentTree = setupComponent(ui, renderOptions); return render(componentTree); } catch (error) { console.log(error); throw error; } }; export * from '@testing-library/react'; export { customRender, IExtendedRenderOptions };

Usage

This is how we render the component in our Test

// Component.test.tsx import React from 'react'; // we import out customRender import { customRender, screen, fireEvent, waitFor } from 'ns_libs/testing/testUtils'; import Component from 'pages'; const initialProps: IProps = { toggle: () => {}, isOpen: true, target: 'test-popover-target', feature: FEATURES_LIST.PORTFOLIO_GROUPS, subprojectId: 0, }; // initialize redux state with any value you want // maybe same values that is initialized in your app const initialReduxState = { user: { name: "", age: 0, } } const renderUi = (props: IProps = initialProps) => customRender( <Component {...props} />, { withRouter: true, routerHistory: [':some_path_maybe'], withRedux: true, mockInitialState: initialReduxState, }, );

This is an example of passing mock data on mock axios calls, if you still need to use that option to mock axios calls happening in your component:

import React from 'react'; import { customRender, screen, act, waitFor } from 'ns_libs/testing/testUtils'; import '@testing-library/jest-dom'; import axios from 'axios'; // Sample Component const SampleComponent = () => { const [data, setData] = React.useState<any>(); const [dataTwo, setDataTwo] = React.useState<any>(); const getTodos = async (): Promise<any> => { try { const res = await axios.get('https://yourendpoint.todos.com'); setData(res); } catch (error) { console.log(error); } }; const getOtherThings = async (): Promise<any> => { try { const res = await axios.get('https://endpoint.otherthings.com'); setDataTwo(res); } catch (error) { console.log(error); } }; React.useEffect(() => { getTodos(); getOtherThings(); }, []); return ( <div> {data && <h1>{data?.map((item: any) => <span key={item.email}>{item.title}</span>)}</h1>} {dataTwo && <h1>{dataTwo?.map((item: any) => <span key={item.email}>{item.title}</span>)}</h1>} </div> ); }; export default SampleComponent; // Here starts the test for Sample Component const ResponseMock: any = [ { endpointFragment: '/todos', expectedMockResponse: [ { title: 'Nice First Title', description: 'The todo is this', }, title: 'Nice Second Title', description: 'The todo is this', { }, ], }, { endpointFragment: '/otherthings', expectedMockResponse: [ { title: 'First Other', content: 0, }, { title: 'Second Other', content: 0, }, ], }, ]; const renderUi = () => customRender(<SampleComponent />, { mockAxiosCalls: ResponseMock, }); describe('Test Component', () => { afterEach(() => { jest.resetAllMocks(); }); test('First test', async () => { renderUi(); await waitFor(() => { const title = screen.getByText('Nice First Title'); const title2 = screen.getByText('Nice Second Title'); const title3 = screen.getByText('First Other'); const title4 = screen.getByText('Second Other'); expect(title).toBeInTheDocument(); expect(title2).toBeInTheDocument(); expect(title3).toBeInTheDocument(); expect(title4).toBeInTheDocument(); }); }); });

You are watching: React Testing Library custom Render with options for Providers.. Info created by GBee English Center selection and synthesis along with other related topics.