Testing at the right layer: API vs UI
You can often test the same behaviour at more than one layer — through the API, or through the UI that calls it. Choosing the right layer for each check is one of the most valuable judgements a QA makes, because it decides whether your suite is fast and stable or slow and flaky. This lesson brings the pyramid down to a practical rule: test each thing at the lowest layer that can catch the bug, and keep the UI layer thin. It closes the api-automation module and sets up how the capstone divides its tests.
The same behaviour, two layers
Take AgentPay's amount validation: an amount over the limit must be rejected. You could test it two ways:
- Through the API — POST a transaction with
amount: 100001, assert422. Milliseconds, rock solid. - Through the UI — open the form, type 100001, click create, wait for the error banner to appear, assert its text. Seconds, and it can flake on timing or a moved element.
Both check the same rule. The API test is faster, more stable, and pinpoints the failure (the endpoint rejected it) — so the validation rule belongs at the API layer. You do not need a UI test for every validation rule; one or two UI tests prove the form shows an error, and the API tests cover all the rules exhaustively.
The rule: lowest layer that catches the bug
Generalise it. For each thing you want to test, ask: what is the lowest layer that can actually catch this bug? — and test it there.
- Business rules, validation, calculations, permissions, money logic → API (or unit). These live on the server; test them directly where they are fast and stable. AgentPay's rules — amount limits, phone format, role scoping, idempotency, suspended agents, aggregates — are all API tests, and there are many of them because that is cheap.
- A whole user journey works end to end → UI. Logging in, navigating, seeing data render, completing a flow through the screen — this genuinely needs the browser, because the thing under test is the integration of UI + API. Keep these few and focused on the critical journeys.
- How something looks / responsive / cross-browser → UI, because rendering is only visible in a browser (the web-testing module).
The failure mode to avoid is testing a server-side rule through the UI: it is slower, flakier, and when it fails it only says "something on this page is wrong" instead of "the endpoint returned the wrong status". Push the check down.
How AgentPay divides its tests
The reference suite is a worked example of this division, and it is worth studying:
- API tests (21) — carry the bulk of the coverage: every validation rule, every status code, auth and permission (including the id-swap access test), idempotency, filtering/sorting/pagination correctness, the dashboard aggregate. Fast, stable, exhaustive.
- E2E/UI tests (12) — cover the journeys and rendering that need a browser: login and logout, the transactions table actually displaying and filtering/sorting/paginating on screen, creating a transaction through the form and seeing confirmation, and role-based visibility in the UI. Few, focused, each a real user path.
Notice the overlap is deliberate and thin: pagination correctness is proven exhaustively at the API (no row dropped across pages), and the UI has one test that the table paginates on screen. The rule's edge cases live at the API; the UI test proves the wiring. That is a healthy pyramid — a thick, fast API base and a thin, meaningful UI cap — and it is why the whole suite runs in seconds and rarely flakes.
When to duplicate, and when not
A reasonable question: should a check ever exist at both layers? Sometimes:
- Yes, thinly, for a critical path: prove the rule exhaustively at the API and have one UI test that the user actually experiences it (an over-limit amount shows an error on the form). The UI test guards the wiring; the API tests guard the rule.
- No for the long tail: do not re-test all 20 validation cases through the UI. That is duplicated cost and flakiness for no extra confidence — the ice-cream-cone mistake.
The judgement is always the same trade-off from the automation-strategy module: coverage where it is cheap and reliable (API), and only the few genuinely end-to-end checks where it is expensive (UI). Get the layer right for each test, and the suite stays fast, stable and trusted — which is the entire goal of automation.
Check your work
Same behaviour, two layers — a validation rule can be tested via the API (fast, stable, precise) or the UI (slow, flakier, vague on failure). The rule belongs at the API; a UI test only needs to prove the form shows an error, not to re-check every rule.
The rule: test each thing at the lowest layer that can catch the bug. Business rules/validation/ calculations/permissions/money → API or unit; whole user journeys → UI; look/responsive/cross-browser → UI. Never test a server-side rule through the UI (slow, flaky, imprecise) — push it down.
AgentPay's division: 21 API tests carry the exhaustive rule/auth/aggregate coverage; 12 E2E tests cover journeys and rendering that need a browser. Overlap is thin and deliberate (pagination proven at the API, one UI test that it paginates on screen) — a thick API base, a thin UI cap.
Duplicate? Thinly, for critical paths (rule at API + one UI test of the experience); never for the long tail (don't re-test 20 validation cases through the UI — the ice-cream-cone mistake). Coverage where cheap and reliable, UI only where genuinely end-to-end.
Practice
- Take AgentPay's amount-limit rule and write both an API test and a UI test for it; compare speed and reliability, and say where the rule belongs.
- For five AgentPay behaviours, decide the right layer (API, UI, or unit) and justify each.
- Identify a check currently done through the UI that would be better at the API; move it and explain the gain.
- Explain why AgentPay proves pagination correctness at the API but keeps only one UI pagination test.
- Give an example where testing at both layers is worth it, and one where it is wasteful duplication.
- Sketch the layer split you would use for a new feature (say, a refund flow), listing which checks go where.
Official documentation
- Martin Fowler — The practical test pyramid — Choosing the layer and keeping the UI thin.
- Google Testing Blog — Just say no to more end-to-end tests — Why to push checks below the UI.
- Playwright — API testing — Testing behaviour at the API layer.
Next: the mobile-automation module — automating tests on real mobile apps with Appium.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship