Unit tests add value by constraining software design.
Some of these constraints only come into play usefully in conjection with TDD. The following constraints and outcomes can be had with unit tests:
- Composability: Validating code paths from input to output pressures the author to name smaller groups of paths, leading to smaller functions. These are automatically more composeable for driving tighter outcomes.
- Understandability: Similarly, more named behaviors leads to more explicit communication to future readers.
- Simplicity: As above, complexity is reduced, but further through a natural pressure to isolate dependencies outside of most units. Functions are most readily testable without an “outside world” to mock.
- Speed: Especially where TDD is involved, isolation and fast feedback loops are necessary and intermingled parts of the development process.
All of these outcomes help illustrate that constrains optimize for what is valued. If these were unimportant benefits, the difficulty that unit testing adds to software design wouldn’t be worthwhile.