Why CI latency matters
There's a systematic gap between CI and production: your unit tests run with zero-latency mocks, integration tests run against local services, and then production hits real network latency, cache misses, and tail behavior. The bugs that live in this gap — timeout misconfiguration, retry storms, circuit breakers that never trip — only surface in production.
Adding slowdep to your CI pipeline closes this gap. A dedicated "latency tests" CI step runs your integration tests with realistic latency profiles, catching the class of bugs that zero-latency tests structurally cannot find.
If your test suite would still pass if every dependency responded in 0ms, you're not testing latency-dependent behavior. Add slowdep to tests where latency characteristics affect correctness.
Configuring slowdep for CI
Keep latency disabled locally by default so developer iteration stays fast. Enable it in CI via an environment variable. This pattern lets you run the full test suite quickly on a developer machine while ensuring latency tests run on every CI build.
// lib/testHelpers.ts import { withLatency } from 'slowdep'; const LATENCY_ENABLED = process.env.SLOWDEP_ENABLED === 'true'; /** * Wraps a function with realistic latency when SLOWDEP_ENABLED=true. * In local development (default), returns the function unchanged. */ export function maybeSlowdep<T extends (...args: any[]) => Promise<any>>( fn: T, preset: Parameters<typeof withLatency>[1] ): T { return LATENCY_ENABLED ? withLatency(fn, preset) : fn; } // usage in tests const db = maybeSlowdep(realDb.query, 'postgres'); // local: instant | CI (SLOWDEP_ENABLED=true): lognormal p50:5ms p99:200ms
Which tests to add latency to
Not every test benefits from realistic latency. Apply it selectively where latency characteristics actually affect whether the test is meaningful.
| test type | add slowdep? | reason |
|---|---|---|
| unit tests | no | unit tests mock dependencies; latency adds noise without signal |
| integration tests | yes | tests real code paths with realistic dependency behavior |
| retry / timeout tests | yes | these specifically test latency-dependent behavior |
| e2e tests | selectively | add for specific scenarios (slow network, timeout UX); not all flows |
| snapshot tests | no | UI snapshots don't depend on latency timing |
| circuit breaker tests | yes | circuit breakers trip on slow failures, not fast ones |
CI time budget
Realistic latency slows down tests — that's the point, but it needs to be managed. A test that takes 5ms without slowdep might take 200ms with it. At 1000 tests, that's 200 seconds of added time. These strategies keep CI fast.
@latency Jest tag or a separate *.latency.test.ts suffix) and run them as a parallel CI job. Regular tests remain fast; latency tests run alongside them.--maxWorkers or Vitest's --pool=threads to run test files in parallel. Latency tests benefit most from parallelization since they spend most time waiting.Deterministic CI
Probabilistic latency means tests can occasionally fail when tail samples are sampled.
For deterministic CI, seed Math.random at the start of each CI run.
The same seed produces the same latency samples every time, making failures
reliably reproducible.
// jest.setup.ts — seed Math.random for deterministic CI if (process.env.SLOWDEP_SEED) { const seed = parseInt(process.env.SLOWDEP_SEED, 10); // simple seeded PRNG (use a proper implementation in production) let s = seed; Math.random = () => { s = (s * 1664525 + 1013904223) >>> 0; return s / 0xFFFFFFFF; }; } // then in CI: SLOWDEP_SEED=12345 jest // same seed = same latency samples = reproducible failures
Example CI config
A GitHub Actions workflow that runs unit tests and latency tests as parallel jobs. Latency tests only run when there are changes to files that exercise latency-dependent behavior.
# .github/workflows/ci.yml name: CI on: push: branches: [main] pull_request: jobs: unit-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - run: npm ci - run: npm test env: NODE_ENV: test latency-tests: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' - run: npm ci - run: npm run test:latency env: NODE_ENV: test SLOWDEP_ENABLED: 'true' SLOWDEP_SEED: '42' timeout-minutes: 10
Add "test:latency": "jest --testPathPattern=\\.latency\\.test\\.ts$ --maxWorkers=4" to your package.json. Latency tests live in *.latency.test.ts files alongside your regular tests.