The big TDD misunderstanding (2022)
Test-driven development (TDD) and unit testing are scrutinized here, with many arguing that tests tightly coupled to implementation details create brittle suites that hinder refactoring and waste effort. Commenters contrast low-level unit tests with higher-level integration and end-to-end tests, debating where the best cost–benefit lies, how much coverage is actually useful, and whether tests should primarily validate behavior from a user or system boundary. Underneath is a broader concern about dogmatic methodologies: some see TDD as a powerful design aid when applied thoughtfully, while others treat it as situational tooling that must be chosen based on context, quality goals, and system complexity.
Meaning of “unit” in unit testing
- Article’s claim that “unit” originally referred to an isolated test is heavily disputed.
- Several commenters cite earlier definitions (e.g., “small compilable module, ~100 LOC”) where the unit is clearly the code under test, not the test.
- Consensus in the thread: language has drifted, but most practitioners and historical documents treat the unit as a code component (function, class, module), though some accept “independent test” as a useful modern notion.
Is TDD about testing or design?
- One camp: the big misunderstanding is treating TDD as testing; it’s mainly a design practice that uses tests to shape APIs and separation of concerns.
- Opposing camp: TDD-as-design is overrated or harmful; design skill and judgment matter more, and TDD offers weak design guidance, especially for less experienced devs.
- Some split the term: Test-Driven Development vs Test-Driven Design, arguing they’re often conflated.
Unit vs integration/E2E tests
- Many argue high-level functional/integration tests provide better value:
- Closer to user-visible behavior.
- More stable across refactors.
- Avoid over-coupling tests to implementation and mocking sprawl.
- Others defend the traditional testing pyramid: numerous fast, isolated unit tests catch edge cases and bugs that broader tests may miss, especially for complex algorithms or libraries.
- Common compromise:
- Unit tests for pure, complex logic or reusable foundations.
- Integration/E2E tests for business workflows and HTTP/JSON or UI boundaries.
Fragile tests and refactoring pain
- Frequent complaint: refactoring “without changing behavior” breaks many low-level tests.
- Diagnoses: tests are too white-box, over-mocking, or asserting irrelevant internal details.
- Strategies mentioned:
- Focus tests on externally observable behavior.
- Accept that some early, implementation-focused tests should be deleted or evolved once higher-level behavior tests exist.
- Use tests more as a safety harness for refactoring than as design constraints.
Dogmatism, workflow, and metrics
- Debate over “never change code without a red test”: some see it as core to TDD, others as unnecessary ritual, especially during exploratory work.
- Broad skepticism about cargo-cult TDD, mandatory 100% coverage, and coverage as KPI (Goodhart’s law).
- Several emphasize context: domain risk, language (dynamic vs strongly typed), non-functional requirements, and team skill should drive test strategy, not a single universal methodology.