Skip to content

Testing Strategy

I need to... Guide Summary
Understand the cost/confidence/speed model and risk-based investment Testing Strategy Overview Testing is a cost/confidence tradeoff, not a boolean. Use risk-based investment — test money, auth, and complex logic heavily; test trivial code lightly. Goodhart's Law applies — coverage targets without quality enforcement catch nothing.
Choose between Pyramid and Trophy for my stack Test Pyramid vs. Trophy Use the Pyramid (many unit tests) for logic-heavy backends and PHP/Drupal; use the Trophy (mostly integration) for React/Next.js frontends. The right shape is the one that catches the bugs your stack actually produces.
Know what belongs in a unit test and the FIRST properties Unit Testing Concepts Unit tests verify one function or class in isolation — no DB, no HTTP, no filesystem. Apply FIRST properties (Fast, Isolated, Repeatable, Self-validating, Timely). Test observable behavior, not private details; one logical assertion per test.
Know what integration tests cover and where their boundaries sit Integration Testing Concepts Integration tests verify seams between components using a real test database and real internal implementations, stubbing only external third-party services. They deliver the highest ROI per test in most modern applications.
Know what functional/system tests cover vs. integration tests Functional Testing Concepts Functional tests verify feature behavior via HTTP or equivalent without a browser — real stack, real DB, no browser rendering engine. Use for permission enforcement, multi-step workflows, and feature regressions that integration tests cannot easily exercise.
Understand E2E tests: value, cost, and when to add them E2E Testing Concepts E2E tests verify complete user journeys through a real browser — use them sparingly for critical paths only. 10–100x slower than integration tests; use data-testid selectors and never test logic variants in E2E.
Use visual regression testing without drowning in noise Visual Regression Concepts VR testing captures screenshots and diffs pixels against a baseline. Earns its keep for stable component libraries; adds friction when UI is still changing. Requires deterministic rendering — disable animations, pin browser versions.
Test API contracts and service boundaries Contract & API Testing Concepts Contract tests encode what a consumer expects from a provider, letting each deploy independently. Justified when different teams own consumer and provider; overkill for same-repo services. Schema validation via OpenAPI is a simpler alternative.
Test performance systematically without polluting unit tests Performance Testing Concepts Run performance tests in a dedicated CI stage on a stable environment, never in the unit suite. Set explicit budgets (P95 latency, Core Web Vitals) and enforce them. Always profile before optimizing.
Test accessibility — what automation covers and what it cannot Accessibility Testing Concepts Automated a11y tools catch ~35% of detectable WCAG issues. The rest requires manual keyboard navigation and screen reader testing. Add axe-playwright to E2E tests and jest-axe to component tests.
Choose the right test double: stub, mock, spy, fake, or dummy Test Doubles Use stubs to return controlled values, mocks to verify side effects occurred, fakes for realistic in-memory alternatives. Never mock your own internal code — mock only at the boundary (external services).
Structure tests for readability and one-assertion clarity Test Structure and Naming Structure every test as Arrange/Act/Assert with blank lines between phases — one Act per test. Name tests to describe scenario and expected outcome. Multiple asserts are fine if they verify facets of the same single outcome.
Fix or prevent flaky, non-deterministic tests Determinism and Flakiness A suite with 5% flakiness has 99.9% chance of at least one failure per run. Root causes: time/date, randomness, shared state, async timing, real network. Fix by injecting clock/random dependencies and waiting for specific conditions rather than sleeping.
Use coverage metrics correctly without gaming the number Coverage Philosophy Coverage tells you which lines were NOT executed, not whether tests verify correct behavior. Use branch coverage over line coverage. Mutation testing is the most reliable quality signal for critical modules.
Decide what is worth testing and what is not What to Test and What Not To Test observable behavior, not implementation details. High-risk code (auth, money, complex logic, API boundaries) deserves heavy investment; framework internals, trivial delegation, and generated code get none.
Identify and fix the most costly testing anti-patterns Best Practices and Anti-Patterns Tests are production code — apply the same review standards. Key anti-patterns: The Liar, The Mockery, The Sleeper, The Optimist. Every feature needs explicit security test cases for auth, permissions, and input handling.
Map any verification need to the right test type quickly Choosing a Test Type Master decision matrix mapping verification needs to test types. Most features need a combination: unit for logic, integration for seams, E2E for critical journeys only. Quick reference by stack included.