When a change needs quick, trustworthy feedback, use many fast unit tests as the foundation and fewer broader tests above them. Unit tests check isolated building blocks; integration tests check interactions between units and dependencies; end-to-end tests check the complete system. section 3.4
Move upward only after the lower layer is healthy:
- Start with deterministic unit tests around small algorithmic functions. Keep them close to the code because they are fast, understandable, and easy to debug. section 11.4.2
- Add integration tests to verify functionality between units, including connections to services such as databases. section 3.4
- Use a smaller layer of system or end-to-end tests for complete behavior, with specialized forms such as load testing when needed. section 3.4
- Run the layers in phases and do not proceed upward when unit tests fail; higher layers cost more, depend on more resources, and can be less predictable. section 3.4
A practical starting point
For a balance or error-rate calculation, supply fixed data to a unit test and expect the same result each time. Use mocking where dependencies would otherwise obscure the calculation, then add integration coverage for larger services and reserve full-system checks for complete workflows. section 11.4.2
Figure 3.2 End-to-end tests are smaller at the top because they are more expensive and not as dependable. They should be supported by larger suites of integration and unit tests. Each layer should run on its own. Start with unit tests and progress up the pyramid in different phases.
Use the pyramid diagram to compare test-suite size, cost, dependability, and phased execution across unit, integration, and end-to-end layers. section 3.4







