A test suite that tests everything at the same layer is either too slow or too brittle. A test suite with no strategy is a collection of accidents. The testing pyramid provides a simple mental model: many fast, cheap unit tests at the base; a moderate number of integration tests in the middle; a small number of slow, expensive end-to-end tests at the top. For a full-stack FastAPI + React application, this pyramid spans both sides — Python tests on the backend, JavaScript tests on the frontend, and Playwright E2E tests that drive both together.
Full-Stack Testing Pyramid
▲
/E\
/ 2 \ End-to-End: Playwright
/ E \ · Critical user flows (login, create post, like)
/ T \ · Slow (~30s per test), few (10–20 tests)
/─────────\ · Run in CI nightly or on release branches
/ \
/ Integration \ Integration: pytest + React Testing Library
/ \ · API endpoints with real test DB
/ ~30–50% \ · Components with mocked API (MSW)
/ \ · Medium speed, moderate count
/─────────────────────\
/ \
/ Unit \ Unit: pytest + Vitest
/ ~50–60% \ · Pure functions, reducers, validators
/ \ · Business logic, schemas
/ \ · Fast (<1s), many (100+)
─────────────────────────────────
BACKEND TEST LAYERS:
Unit: Pydantic validators, utility functions, reducer-style logic
Integration: FastAPI endpoints via TestClient with test PostgreSQL
E2E: Full HTTP stack via Playwright
FRONTEND TEST LAYERS:
Unit: Redux reducers, custom hook logic, utility functions
Component: React components via RTL with MSW API mocking
E2E: Playwright driving the browser against real backend
What to Test at Each Layer
| Layer | Test | Do Not Test Here |
|---|---|---|
| Backend Unit | Pydantic validators, password hashing, slug generation, token expiry logic | Database queries, HTTP routing |
| Backend Integration | All API endpoints: status codes, response shape, DB state after mutations, auth guards | Pure business logic covered by unit tests |
| Frontend Unit | Redux reducers, custom hooks (with mock), utility functions like normaliseApiError | DOM rendering, API calls |
| Frontend Component | PostCard renders correctly, form shows errors, login redirects after success | Cross-component navigation flow, backend logic |
| E2E | Register → login → create post → verify in feed, like post → notification appears | Edge cases, error messages, individual component behaviour |
Common Mistakes
Mistake 1 — E2E testing what unit tests should cover
❌ Wrong — Playwright test that verifies slug validation rules:
Each validation rule tested via Playwright takes ~15 seconds and breaks whenever the UI changes. Test the validator function directly in a 5ms unit test.
✅ Correct — one E2E test verifying the happy path; unit tests for all validation rules.
Mistake 2 — No integration tests (unit tests pass, API is broken)
❌ Wrong — 100% unit test coverage but the SQL queries are wrong:
Unit tests with mocked DB sessions cannot catch incorrect SQL, missing JOIN conditions, or wrong column names.
✅ Correct — integration tests against a real test database catch these issues.