Slow jest test suite ?

Notes on how I speeded up unit test execution during build time

3 min read

.

Feb 14, 2023


My Notes

  1. Implement caching in GHA workflow If you are working on a relatively large code base. Possibly a monorepo, there could be hundreds or thousands of test cases that need to be run. Caching could really speed up the build time. Idea is that you could create caching on PR level and caching nightly as well to preserve and use later.

    • Cron job to run and create caching nightly
name: Cache Cron unit tests
on:
  workflow_dispatch:
      inputs:
          jestConfigs:
              description: 'Additional jest condigs (Eg: Ignore failing tests)'
              required: false
  schedule:
      - cron: '0 0 * * *'

jobs:
  unit-test-nightly-cache:
      name: Unit tests
      runs-on: [self-hosted, heavy]
      timeout-minutes: 120
      steps:
          - uses: actions/checkout@v3
            with:
                fetch-depth: 0

          - name: Install dependencies
            uses: ./.github/actions/install-deps

          - name: Get current date
            id: date
            run: echo "::set-output name=date::$(date +'%Y-%m-%d')"

          - name: Increase node memmory
            shell: bash
            run: node --max-old-space-size=16192

          - name: Cache Jest nightly
            id: cache-jest-nightly
            uses: actions/cache@v3
            with:
                path: '**/.jest_cache'
                key: ${{ runner.os }}-jest-${{ steps.date.outputs.date }}

          - name: Start test
            working-directory: ./
            shell: bash
            run: NODE_OPTIONS=--max_old_space_size=16192 yarn test --cacheDirectory=".jest_cache" --ci --silent --forceExit ${{ github.event.inputs.jestConfigs}}
  • Restore cache
unit-test:
    name: Unit tests
    runs-on: [self-hosted, heavy]
    timeout-minutes: 15
    needs: install-dependencies
    steps:
        - uses: actions/checkout@v3
          with:
              fetch-depth: 0

        - name: Install dependencies
          uses: ./.github/actions/install-deps

        - name: Restore Jest cache
          id: cache-jest-pr
          uses: actions/cache@v3
          with:
              path: .jest_cache
              key: ${{ runner.os }}-${{ github.event.pull_request.number }}-jest
              restore-keys: |
                  - ${{ runner.os }}-${{ github.event.pull_request.number }}-jest
                  - ${{ runner.os }}-jest-${{ steps.date.outputs.date }}
  1. Multiple workers could also speed up the tests drastically. You need to find the optimal number of workers that suit your project. Just increasing workers won’t work. There is a sweet spot that you need to find with regards to workers. There seems to be a noticeable improvement in test run time when introducing --maxWorkers option instead of --runInBand

--maxWorkers, specifies the maximum number of workers the worker-pool will spawn for running tests. By default, it will match the available cores in the hardware. Here the max worker value has been adjusted to cater to resource-limited environment.

--runInBand, run all tests serially in the current process, rather than creating a worker pool of child processes that run tests.

  "test": "NODE_ENV=test yarn jest --passWithNoTests --maxWorkers=2 --config jest.config.json",

A good read:

https://jestjs.io/docs/troubleshooting#tests-are-extremely-slow-on-docker-andor-continuous-integration-ci-server

with --maxWorkers

Image

with --runInBand

Image
  1. Detect and fix memory leaks. There could be memory leaks happening unnoticeably. Use following command to detect them.

    --detect-leaks

  2. Use jsdom when possible By default Create React App uses the jsdom test environment for running tests. As jsdom incurs some setup costs, it is often possible to speed up non-UI test suites, which do not require a DOM object, by setting the test environment to node.

  3. Avoid barrel files - import file directly instead importing index.js

  4. Avoid using wait/sleep statements from tests. Wait /sleep will further slow down tests. If necessary could use proper synchronization techniques/async fn.

  5. Jest is slow on Windows Jest tests run significantly slower on Windows machines than on Mac OS and Linux due to slower crawling of the Windows file system.

  6. Tests should be independently-runnable: tightly-coupled tests slow down the runtime

  7. Best practices in jest https://github.com/testing-library/react-testing-library/issues/819

ByRole can be 40x slower than ByText

  1. Self-contained tests External dependencies can slow down the tests. Eg: calling actual apis. Or PACT.io mocked apis could slow the test run time. Solution is to mock purely using Jest given mocking methods.

  2. Remove obsolete tests and dead code Dead code and obsolete tests waste time and contribute to the project’s technical debt.

  3. Too much Dom testing could slow down the time it takes to run unit tests. Choose in between cypress and jest and avoid duplicating the testing in both integration and unit testing.

  4. Run only changed test. There could be situations that you need to run all the tests. Eg: deploying a feature with breaking changes. But most of the time you only need to run the test that affect your changes.

jest --changedSince <commit-id>