Skip to content

Jest & Vitest Flaky Tests in CI: Causes & Fixes (2026)

Jest and Vitest tests flake from leaked module state, mocks, and timers. Reset state between tests, fake the clock, and detect flakiness over runs.

İbrahim Süren
Founder · Jun 25, 2026 · 4 min read
Jest & Vitest Flaky Tests in CI: Causes & Fixes (2026)

Jest and Vitest tests flake mostly from shared module state, mocks and timers leaking between tests, and parallel workers exposing races. Fix it by resetting mocks and modules between tests, using fake timers for time-dependent code, isolating global state, and detecting which tests flake from history across CI runs rather than one execution.

Key takeaways

  • Leaked state is the top cause — mocks, modules, and globals carrying over between tests.
  • Use clearMocks/resetModules (or Vitest equivalents) so each test starts clean.
  • Replace real timers and dates with fake timers to make time-dependent tests deterministic.
  • Parallel workers expose races; avoid shared files, ports, and global singletons.
  • Detect flakiness from JUnit/JSON history across runs, not a single result.

Jest and Vitest are fast, isolated test runners — but “isolated” depends on you keeping state from leaking, and CI is where leaks show up. When unit tests pass locally and flake in the pipeline, the cause is usually shared state, real timers, or a race that parallel workers expose. This guide covers the fixes for both runners (they share most of the same patterns). For the framework-agnostic view, see the complete guide to flaky tests. Where we reference Qualflare, we describe only what it actually does.

Why Jest and Vitest tests flake

The recurring causes, often surfacing only in CI versus local:

  • Leaked state — mocks, module state, or globals carrying over between tests.
  • Real timers and dates — code that depends on the clock behaving differently on a slower runner.
  • Parallel workers — Jest and Vitest run test files in parallel, exposing shared-resource races.

All three are forms of non-determinism: the result depends on state the test doesn’t fully control.

The fixes

Reset state between tests

A test that mutates a mock, a module, or a global can quietly change the outcome of the next one. Make each test start clean:

// jest.config.js
module.exports = { clearMocks: true, restoreMocks: true };

For tests that depend on fresh module state, call jest.resetModules() (Vitest: vi.resetModules()) and re-import in beforeEach. Reset any singleton or global you touch. The goal is that test order never changes a result — running with Jest’s --shuffle flag (or Vitest’s sequence.shuffle option) will quickly confirm or refute that.

Control time with fake timers

Code using setTimeout, Date.now(), or debounce/throttle is timing-sensitive by nature. Fake timers make it deterministic:

jest.useFakeTimers();              // vi.useFakeTimers() in Vitest
// ... trigger the timed behavior ...
jest.advanceTimersByTime(1000);    // advance explicitly, no real waiting

This removes dependence on how fast the CI machine runs.

Make tests parallel-safe

Both runners parallelize across workers. Tests that write to a shared file, bind a fixed port, or mutate a shared external resource will race. Use per-test temp paths and unique identifiers, and mock external services rather than hitting them. If a specific file genuinely can’t be parallelized, isolate it rather than forcing the whole suite to run in band (--runInBand).

Detect flakiness over time

Flakiness is common enough to plan for — Google reported that almost 16% of its tests show some level of flakiness (Google Testing Blog, 2016). Resetting state fixes the common cases, but residual flaky tests need to be found and tracked — and a single run can’t reveal them. Emit JUnit (via jest-junit) or JSON results on every CI run and send them to an observability layer that builds pass/fail history and scores flakiness. See Jest test reporting. Qualflare aggregates Jest and Vitest results across every run — including across monorepo packages — scores flakiness, and clusters related failures by cause.

Start free with Qualflare — send your Jest or Vitest results and get flaky scoring and failure clustering on your own suite.

Flaky tests in other frameworks: Playwright, Cypress, and pytest.

Frequently asked questions

Why are my Jest tests flaky in CI?

Usually leaked state between tests — mocks, module state, or globals carrying over — plus time-dependent code using real timers, and races exposed by parallel workers on slower CI machines. Resetting mocks and modules between tests, using fake timers, and isolating shared resources fixes most cases.

How do I reset state between Jest tests?

Enable clearMocks (and resetMocks/restoreMocks as needed) in your Jest config, use jest.resetModules() when tests depend on fresh module state, and reset any global or singleton you mutate in beforeEach. Vitest offers the same with clearMocks/restoreMocks and vi.resetModules().

How do I make time-dependent tests deterministic?

Use fake timers — jest.useFakeTimers() (or vi.useFakeTimers() in Vitest) — and advance time explicitly with jest.advanceTimersByTime(). This removes dependence on the real clock and on how fast the CI machine runs, which is a common source of flakiness.

How do I detect flaky Jest or Vitest tests?

Output JUnit (jest-junit) or JSON results on every CI run and send them to a test observability tool that tracks each test’s pass/fail history and scores flakiness. Flakiness is only visible across many runs, so single-run output can’t reveal it.

Ready to ship with confidence?

Start free with Qualflare's AI-powered test management.