Q28 of 37 · API testing
How do you decide what to cover at the API layer vs the UI layer to avoid duplication?
Short answer
Short answer: Cover business logic, validation, auth, and edge cases at the API layer (fast, deterministic). Cover only UI-specific concerns at the UI layer: rendering, conditional UI, accessibility, the golden-path user journey. If a behaviour can be tested either way, prefer API.
Detail
Every team eventually has overlapping coverage — the same flow tested at three layers. The right split saves time without losing coverage.
The principle: each layer tests what only it can test. Lower layers test logic; UI tests render and interaction.
At the API layer (most coverage):
- Business logic: "discount applies when total > £50."
- Validation: required fields, format checks, ranges.
- Auth & permissions: who can do what, scope enforcement.
- Edge cases: null handling, empty arrays, very long strings.
- Error paths: 400/401/403/404/500 conditions.
- Schema integrity.
- Rate limits, idempotency keys, async job lifecycle.
At the UI layer (selective):
- The golden-path user journey: signup → buy → confirmation.
- Conditional rendering (does the right component show?).
- Form behaviour (disabled until valid, error display).
- Accessibility — keyboard nav, screen-reader hooks, focus management.
- UI state machines that aren't reflected in API state — modals, drawers, toasts.
- Visual regression for critical screens (sparingly).
What does not belong at the UI layer:
- "Wrong password shows error" — covered by API + a single UI test that asserts the display of an error, not the auth logic.
- "Validation rejects invalid email" — API tests cover the logic; UI test covers that the error appears at all.
- "Admins see admin menu, viewers don't" — auth covered at API; UI test covers conditional render of the menu.
The duplication trap:
A common pattern: a UI test for "user can update name" walks through login, navigation, form fill, save — 30 seconds. The same coverage at the API layer takes 50ms. The UI test still exists "for confidence." This is the leak.
The fix: for every UI test, ask:
- What does this verify that an API test can't?
- If "rendering," "interaction," or "user journey" — keep it.
- If "logic," "validation," or "error message text" — move to API.
Numerically:
- API tests: hundreds to thousands. Aim for 80-90% endpoint × scenario coverage.
- UI tests: dozens to ~100. Tightly focused on user journeys and rendering.
Tooling: same framework where possible. Playwright covers both via APIRequestContext + browser context. Cypress also covers both with cy.request. Sharing the runner means shared CI, shared reports, and shared fixtures.
The conversation with PMs: "We have an API test for 'can't add to cart with invalid quantity.' Do we also need a UI test? Yes — but only one, asserting the inline error displays. Not five for every quantity edge case."
The cultural signal: the QA team treats the test pyramid as a contract, not a slogan. Pull requests that add UI tests for behaviours covered at API are flagged in code review.