Home / Articles / Shortening Jest Runs Locally and in CI: Workers, Caching and Scope

This article is published in English.

Shortening Jest Runs Locally and in CI: Workers, Caching and Scope

A practical checklist for faster Jest suites: measuring first, tuning workers, trimming global setup, reusing the cache, isolatedModules, and running only affected tests.

956 words

A slow test suite quietly changes how a team works: people run tests less often, push to CI to find out, and wait longest exactly when they can least afford to, during a production hotfix. That cost grows when AI coding assistants generate changes quickly and the suite is the main safety net. Jest ships with many configuration options and sensible defaults, so the out-of-the-box experience is usually fine, but a handful of adjustments can cut run times noticeably both on a laptop and in CI. None of them is exotic; together they make a useful checklist.

Measure before you tune

Every change below has a cost or a trade-off, so start with numbers. Time a full run with the cache disabled (jest --no-cache) to get a cold baseline, then apply one change at a time and measure again.

Tune parallelism to the machine

Jest runs test files in parallel worker processes by default, which is usually good, but not always optimal. Two flags control it:

  • --runInBand runs all tests serially in the current process with no workers. This can be faster for server-side projects whose tests share an expensive resource, or on CI runners with very few cores, where spawning workers costs more than it saves.
  • --maxWorkers sets how many workers Jest spawns. It accepts a number or a percentage of available cores; 50% is a reasonable starting point that leaves headroom for the rest of the machine.

The right value depends on the hardware, so measure locally and on your CI runners separately. CI machines often report more cores than they can actually use under load, and oversubscribing them makes tests slower, not faster.

Keep global setup lean

A global setup file is convenient in a large codebase: register mocks, polyfills and test utilities once, and every test gets them. The catch is that every test file pays for all of it, including files that need none of it. Heavy imports, database fixtures or large mock registries in setupFilesAfterEnv can turn otherwise millisecond-fast unit tests into slow ones.

Move expensive setup closer to the tests that need it: a helper imported explicitly, a beforeAll in the relevant file, or a separate Jest project with its own setup for integration tests.

Reuse the cache

Second runs are usually faster than first runs because Jest caches transformed files and other metadata. You notice this most in watch mode, but CI benefits too if the cache survives between jobs. Point cacheDirectory at a stable path and persist it with your CI system's caching feature, keyed on the lockfile and Jest config, so each pipeline does not start cold.

Use watch mode locally

For local work, jest --watch is the best feedback loop Jest offers. It reruns only tests related to changed files, and its interactive prompt lets you filter by file name or test name pattern. It is not meant for CI: a pipeline needs a single run that exits with a status code, so keep watch mode on developer machines.

Enable isolatedModules for TypeScript

When TypeScript tests go through ts-jest, full type-checking of every file adds noticeable overhead. Enabling isolatedModules makes the transformer compile each file on its own, without type information from the rest of the program. Teams have reported clear speedups from this in Angular projects, and you give up very little safety as long as tsc --noEmit or your editor still type-checks the codebase. Where exactly the option lives depends on your ts-jest version, so check its current documentation.

Test only what a change affects

There is no reason to run the entire suite for a change that touches one package. Jest itself can narrow the run with --onlyChanged or --changedSince=<branch>, which use version control to find related tests. In a monorepo, a build system such as Nx goes further by understanding the project graph and running tests only for projects affected by the current change.

Keep at least one full run somewhere, for example on the main branch or nightly, to catch anything the dependency analysis misses.

Consider Vitest

Vitest is largely compatible with the Jest API, actively maintained, and works with the common JavaScript frameworks, including Nuxt. For projects already built on Vite it is often the more natural choice, and many teams now reach for it first on new projects. Most of the advice above, measuring, worker limits, lean setup and running only affected tests, applies to Vitest just as well. If you are considering dropping a third-party runner entirely, see replacing Jest with Node's native test runner.

When software tuning runs out

Eventually no configuration change beats faster hardware. A newer laptop or a larger, self-hosted CI runner can be the cheapest remaining improvement once the suite itself is in good shape.

Key takeaways

  • Establish a cold, uncached baseline and change one thing at a time.
  • Match --maxWorkers or --runInBand to each environment's real capacity.
  • Push expensive setup out of global hooks and into the tests that need it.
  • Persist the Jest cache across CI runs; use watch mode only locally.
  • Let isolatedModules skip per-file type-checking while a separate tsc step keeps types honest.
  • Run only affected tests on feature branches, and a full suite on main.