Testing Fundamentals
The difference between "I think this works" and "I can prove this works, repeatedly, automatically."
7.4.1Definition
Automated testing is code that verifies other code behaves correctly, run repeatably without manual re-checking. It spans several levels — unit tests (a single function in isolation), integration tests (multiple components working together), and end-to-end tests (a full user flow through the real system) — each catching different categories of bugs.
7.4.2Why It Exists
Manually re-verifying every part of an application after every change doesn't scale past a small codebase, and human testers reliably miss the same edge cases a machine would catch every time. Automated tests exist to make correctness verification fast, repeatable, and immune to human fatigue — running the same exhaustive checks in seconds that manual testing would take hours to repeat.
7.4.3The Testing Pyramid
| Level | Scope | Speed | Volume |
|---|---|---|---|
| Unit | Single function/module in isolation | Milliseconds | Many |
| Integration | Multiple components together (e.g., API + database) | Seconds | Moderate |
| End-to-end | Full user flow through the real system | Minutes | Few, highest-value flows only |
7.4.4Common Mistakes
- Testing only the happy path, with no coverage for invalid input, empty states, or failure conditions — precisely the cases most likely to break in production.
- An inverted pyramid — mostly slow, brittle end-to-end tests and few fast unit tests, making the whole suite slow to run and painful to maintain.
- Tests that depend on execution order or shared mutable state, passing individually but failing unpredictably when run together.
- Treating a passing test suite as proof of correctness for behavior the suite never actually tests, giving false confidence.
7.4.5Best Practices
- Write the bulk of tests at the unit level, reserving slower integration and end-to-end tests for the highest-value flows.
- Explicitly test edge cases, invalid input, and failure paths, not just the expected successful case.
- Keep each test independent and self-contained, with no reliance on shared state or execution order.