Script Valley
Debugging: A Systematic Approach
Testing as a Debugging ToolLesson 5.5

How to debug flaky tests -- tests that pass and fail randomly

flaky tests, timing dependencies, test isolation failures, shared state, async race conditions in tests, quarantining flaky tests

Flaky Tests Are Bugs in Your Tests

A test that sometimes passes and sometimes fails is not giving you reliable information. It wastes time, erodes trust in the test suite, and often indicates a real production bug -- one that occurs intermittently in the same way. Treat a flaky test as a bug to fix, not noise to ignore.

The Three Root Causes

Flaky tests have three common causes. First: shared state between tests -- one test modifies global data that another test expects to be fresh. Fix: reset all shared state in a beforeEach hook. Second: timing dependency -- a test assumes an async operation completes in a certain time. Fix: await the operation explicitly, do not use setTimeout to wait. Third: dependency on external services -- network calls return different results. Fix: mock external dependencies.

// Flaky: depends on timing
it('processes request', () => {
  submitRequest();
  setTimeout(() => {
    expect(result).toBe('done'); // fails if processing takes longer
  }, 100);
});

// Fixed: await properly
it('processes request', async () => {
  await submitRequest();
  expect(result).toBe('done'); // deterministic
});

// Shared state fix
let db;
beforeEach(async () => {
  db = await createTestDatabase(); // fresh DB per test
});
afterEach(async () => {
  await db.destroy();
});

Quarantine Before Fix

If a flaky test is blocking CI, quarantine it -- mark it as skipped with a bug ticket reference -- rather than deleting it. It documents a real intermittent issue. Fix the underlying cause (shared state, timing, external dependency) and then restore the test.

How to debug flaky tests -- tests that pass and fail randomly — Testing as a Debugging Tool — Debugging: A Systematic Approach — Script Valley — Script Valley