Q33 of 37 · API testing

How would you measure and improve API test execution speed?

API testingSeniorapiperformancecioptimisationsenior

Short answer

Short answer: Measure first: per-test runtime, parallelism utilisation, network time vs setup time. Optimise: parallel runs, persistent connections, shared auth tokens, scoped fixtures (per-suite not per-test), and removing tests that aren't pulling weight. Aim for full suite under 5 minutes; smoke under 60s.

Detail

An API test suite that takes 30 minutes is a suite that gets bypassed. Speed is a quality metric.

Step 1 — Measure. Per-test runtime, sorted descending, gives you the optimisation list. Most frameworks expose this:

pytest --durations=20
mocha --reporter min
mvn surefire:test -DforkCount=4

Capture also:

  • CPU / parallelism utilisation (htop during the run).
  • Time spent in network vs setup vs teardown vs assertions.
  • Test count over time (is the suite growing faster than it's optimising?).

Step 2 — High-leverage optimisations, ranked:

1. Parallelise. Most API test suites are network-bound — perfect for parallel runs. Set workers to N cores and verify tests are isolated (see test-isolation question).

2. Persistent HTTP connections. Reuse keep-alive sockets:

const ctx = await request.newContext({ baseURL });   // shared per worker

Eliminates TLS handshake per call.

3. Cached auth tokens. The biggest single win for tests with auth. Cache per worker:

let token: string;
beforeAll(async () => { token = await getToken(); });

A 1000-test suite that re-auths each test wastes ~10s per test on the auth round-trip.

4. Scoped fixtures. Don't createUser per test if the user is read-only — create per describe. Save 80% of setup time without sacrificing isolation.

5. Shared base data. Read-only fixtures (a static demo organisation) created once at suite startup, deleted at end. Many tests read; few mutate. Mutate-tests get their own fixture.

6. Remove dead weight. The brutal one. Tests that:

  • Test things already covered by lower-layer tests.
  • Test deprecated features.
  • Have always passed (no signal in 6 months). Cut them. Speed is also a hygiene metric.

7. Ship work to the API. Instead of 10 GETs to verify state, ask the API for a single batched response. Reduces N+1 in tests too.

8. Skip costly setup conditionally. If a test only needs an existing user, reuse one across tests in the same describe.

9. Mock external services in unit/component tests, hit the real services only in a small E2E layer. The biggest single source of test slowness is hitting real third parties.

Anti-optimisations:

  • Reducing assertions to "make tests pass faster." You're not optimising; you're losing coverage.
  • Cutting timeouts aggressively — flake skyrockets.
  • Pre-seeding "the test database" globally — gains speed, loses isolation. Already covered as anti-pattern in test-isolation.

Targets:

  • Smoke suite: < 60 seconds. Runs on every PR.
  • Full regression: < 5 minutes. Runs on merge to main.
  • E2E full + UI: < 15 minutes. Runs nightly.

These are aspirational; if you're at 30 minutes, halve to 15 in a quarter, then halve again. Compound returns.

The senior signal: measurement-first, parallelism + caching as the high-leverage moves, and the discipline to delete dead tests rather than carry them.

// WHAT INTERVIEWERS LOOK FOR

Measurement before optimisation, parallelism + token caching as biggest wins, scoped fixtures, and the willingness to delete tests that don't pull weight.

// COMMON PITFALL

Optimising the wrong layer. Spending a sprint sharding tests when 80% of runtime is in re-auth-per-test. Always measure first.