Q27 of 37 · API testing

How would you handle versioning for API tests when the underlying API has v1, v2, and v3 alive?

API testingSeniorapiversioningtest-organisationsenior

Short answer

Short answer: One test suite per version, parameterised by `apiVersion`. Each version has its own contract (schema, expected behaviours) — don't reuse cases across versions assuming behaviour is the same. Track per-version pass rates separately. Sunset old versions on schedule and remove their tests with the API.

Detail

Multi-version APIs are common in SaaS — Stripe, Twilio, AWS — to keep customers happy through breaking changes. The test suite has to handle that gracefully.

The structure:

tests/
  v1/
    users.test.ts      # asserts on v1 contract
    orders.test.ts
  v2/
    users.test.ts      # asserts on v2 contract
    orders.test.ts
  v3/
    users.test.ts      # asserts on v3 contract (current)
  shared/
    auth-helpers.ts    # version-agnostic
    test-fixtures.ts

Per-version configuration:

// tests/v1/users.test.ts
import { test } from '../shared/test';

test.use({ apiVersion: 'v1' });

test('GET /users uses snake_case in v1', async ({ request }) => {
  const res = await request.get('/users/42');
  const body = await res.json();
  expect(body.user_name).toBeDefined();   // snake_case
  expect(body.userName).toBeUndefined();
});
// tests/v2/users.test.ts
test.use({ apiVersion: 'v2' });

test('GET /users uses camelCase in v2', async ({ request }) => {
  const res = await request.get('/users/42');
  const body = await res.json();
  expect(body.userName).toBeDefined();    // camelCase
  expect(body.user_name).toBeUndefined();
});

The fixture (apiVersion) decides whether to send Accept: application/vnd.api+v1, /v1/users, or ?api-version=2026-01-01 — depending on how your API surfaces versions.

Why per-version suites, not parameterised:

  • Fields rename, behaviours change. Forcing one test to handle three versions becomes a tangle of conditionals.
  • Per-version test files document what each version does — useful for support and migration guides.
  • Failures stay scoped — v1's test failing doesn't fail v3's.

Tracking:

  • Pass rate per version per CI run. v1 might be at 99% (legacy customers, stable); v3 at 95% (new development, more churn).
  • Coverage per version. v1 might have 80% endpoint coverage; v3, 100%. Acceptable while v1 is in maintenance mode.
  • Test count over time — should decrease for older versions as endpoints are removed.

Sunset discipline:

  • When v1 sunsets, delete the v1 test directory along with the v1 endpoints. Tests for endpoints that don't exist are technical debt.
  • Communicate the sunset internally a quarter ahead so test owners don't add new v1 tests.

Shared concerns (auth, fixtures, base URL):

  • Live in tests/shared/ and accept the version as a parameter.
  • Avoid version-specific logic in shared code — it ages badly.

The senior signal: separate suites with shared infrastructure, per-version tracking, and the discipline to delete tests when versions sunset.

// WHAT INTERVIEWERS LOOK FOR

Per-version directories, per-version metrics, shared infra without version-specific logic, and sunset → delete-tests rather than letting v1 tests linger.

// COMMON PITFALL

Trying to parameterise a single test to handle three versions. The conditionals proliferate and the test stops documenting any version's contract clearly.