Search code examples
reactjsjestjscreate-react-appaws-amplifyreact-testing-library

Why are my React Testing Library tests so slow?


My workplace has largish single page application, it was built in 2020 and uses Create React App for the build tool. Unfortunately, nearly every component takes an excessive amount of time to test. Sometimes it is longer than 300seconds, sometimes 100seconds, the fastest I've ever seen a test complete is 30seconds. Is this normal behaviour, if not, why are these tests taking such a long time?

enter image description here

While this code example is not reproducable, I am hoping that somebody may be able to take a read through it and highlight some possible problem areas:

// required imports from React & 3rd party libraries
import React from 'react';
import { render, screen, within } from '@testing-library/react';
import { API } from 'aws-amplify';

// react component to be tested, plus context and helper functions
import { MyComponent } from './MyComponent';
import { Store, UserContext, FurtherDetailContext } from 'store/store';
import * as helpers from 'helpers';

// mock data
import userContextMock from '../../../tests/fixtures/userContext/userContext.json';
import furtherDetailContextMock from '../../../tests/fixtures/furtherDetailContext/furtherDetailContext.json';
import gqlQueryResponse1 from '../../../tests/fixtures/gql/gqlQueryResponse1.json';
import gqlQueryResponse2 from '../../../tests/fixtures/gql/gqlQueryResponse2.json';
const mockJson = {
    this: 'is',
    an: 'example',
    response: true
};

const arrange = async () => {
    const mockToggleOpen = jest.fn();
    render(
        <Store>
            <UserContext.Provider
                value={{
                    userContext: userContextMock,
                    setUserContext: () => {
                        // do nothing
                    }
                }}>
                <FurtherDetailContext.Provider
                    value={{
                        furtherDetailContext: furtherDetailContextMock,
                        setFurtherDetailContext: () => {
                            /*do nothing */
                        }
                    }}>
                    <MyComponent myProps={mockJson} toggleOpen={mockToggleOpen} />
                </FurtherDetailContext.Provider>
            </UserContext.Provider>
        </Store>
    );

    // this component performs a network request before rendering
    await screen.findByText('text on the screen');
    const testid1 = screen.getByTestId('test-id-example');
    const testid2 = screen.getByTestId('test-id-example-2');
    return { testid1, testid2 };
};

describe('MyComponent', () => {
    beforeEach(() => {
        // get auth token
        jest.spyOn(helpers, 'getAuthtoken').mockResolvedValue('validAuthToken');
    });
    describe('all calls mocked', () => {
        beforeEach(() => {
            // mock graphql requests
            API.graphql = jest.fn().mockImplementation((url) => {
                const gqlQuery = url.query;
                if (RegExp('.*.mockGqlQuery*').test(gqlQuery)) {
                    return gqlQueryResponse1;
                }
                if (RegExp('.*.mockGql2*').test(gqlQuery)) {
                    return gqlQueryResponse2;
                }

                return;
            });
        });

        it('shows the original text on component mount', async () => {
            const { testid1 } = await arrange();
            expect(within(testid1).getByText('this text should be here')).toBeInTheDocument();
        });
    });
});

Here are the relevant dependencies:

"dependencies": {
  "@types/node": "^12.12.50",
  "@types/react": "^17.0.52",
  "@types/react-dom": "^17.0.18",
  "@types/react-router-dom": "^5.1.7",
  "@types/react-leaflet": "^2.8.1",
  "aws-amplify": "^4.3.26",
  "leaflet": "^1.7.1",
  "react": "^17.0.1",
  "react-dom": "^17.0.1",
  "react-leaflet": ">=3.1.0 <3.2.0 || ^3.2.1",
  "@react-leaflet/core": ">=1.0.0 <1.1.0 || ^1.1.1",
  "react-router-dom": "^6.4.3",
  "typescript": "^3.7.5",
}
"devDependencies": {
  "react-scripts": "^5.0.1",
  "@testing-library/dom": "^7.28.1",
  "@testing-library/jest-dom": "^5.11.6",
  "@testing-library/react": "^12.1.5",
  "@testing-library/user-event": "^13.5.0",
  "@types/jest": "^26.0.4",
  "jest": "^26.6.3",
  "node-sass": "^6.0.1",
}

local development node version: v16.15.1


Solution

  • It's a well known issue that create-react-app is quite slow; and this issue is also extended to the tests.

    The problem is probably webpack, your bundler program, sadly you can't easily decouple an app built with create-react-app from it.

    My recommendation is to switch over to vite, and then to use vite's testing library: vitest in particular.

    (I believe you might keep your normal create-react-scripts and only use vitetest for testing.)

    I understand that this is not the type of answer you might like to receive - it's not only Yet Another Dependency it also means changing a lot of the plumbing; but overall I found it incredibly useful to switch from create-react-app to vite; the speed difference is night and day.

    Here's a migration guide for vitest that you might find useful.