Skip to content

Designing for Testability

Status: ✅ 8-file suite complete (junior · middle · senior · professional · interview · tasks · find-bug · optimize)

How to shape production code so it is easy to test — the design side of testing. Distinct from Unit Tests (which is about writing good tests): this chapter is about the structural choices — dependency injection, seams, pure cores, ports & adapters, the humble object — that make code testable in the first place, before a single test is written. Testability is a proxy for good decoupling: code that is hard to test is usually badly designed.

The positive rules will cover: depending on injected abstractions rather than concrete globals, isolating side effects behind seams (Michael Feathers), pushing logic into a pure functional core, the Humble Object pattern for untestable boundaries, and separating object construction from business logic.

Anti-Patterns to Cover

  • Hidden dependenciesnew-ing collaborators or calling statics/singletons inside a method
  • Global mutable state that test cases leak into each other
  • Un-injected time.Now() / random / I/O making behavior non-deterministic
  • God constructors that do real work (network, DB) on instantiation
  • Logic trapped in an untestable layer (UI handler, framework callback, main)
  • No seams — concrete types with no interface to substitute a test double
  • Static/utility classes holding behavior that can't be faked
  • Testing through too many layers because the unit can't be isolated

See the chapter README for the positive rules.