Q27 of 37 · API testing
How would you handle versioning for API tests when the underlying API has v1, v2, and v3 alive?
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.