TDD exposes your problems, rather than fix them.
Signal-to-noise in a sea of possible implementation
I mentioned on LinkedIn today how we’re spinning in circles discussing before- or after-testing. Discussing the wrong side of the coin, the tooling space rather than the problem space. But first, let’s talk about code organisation.
Vertical Slices & Use Cases
I propose we change our terminology from "units" and "suites" to Use Cases and Implementations.
Why am I so pedantic about this?
When we write "tests" for "features" after the fact, we're not really testing the old code. Instead, we will make behavioural and structural changes to make it testable. This removes the old code and re-shapes it before being tested. This is NOT refactoring. It is a transformation.
Refactoring x Transformation
Refactoring is a systematic process of improving code without creating new functionality that can transform a mess into clean code and simple design.
- refactoring.guru
vs.
Transformations are simple operations that change the behavior of code. Transformations can be used as the sole means for passing the currently failing test in the
red/green/refactor
cycle [of TDD].
- R.C. Martin blog
What kind of vertical slices are Use Cases and what do they have to do with testing?
Use Cases are high-level structures dictating a standalone folder structure and physical separation for the means of keeping high cohesion. Ideally you’d want no dangling dependencies in between Two Use Cases. Think of it like an Application-level subdivision of a Bounded Context. See below article for more detail 👇
TDD, Testing and Refactoring
For the sake of testing code organisation, a team can fashion use case code comprising:
context, and business definition
tests & acceptance tests
In contrast, implementation is:
one of many ways to tactically couple a piece of code to operate a use case
a composition root for a concrete infrastructure-stack (ie. like a DI container for production vs. mocks for testing)
We keep talking about "units" and "test suites" but no product manager uses this terminology willingly.
Why does this matter?
Instead of saying "Writing tests for features after deployment"...
We would say "We write the use case definition for our implementation"
Instead of saying "We write tests, then implement it in react"
We would say "We explore the Use Case, write it down. Then iterate and shape one or many implementations for it."
This is the final point you’ve been waiting for.
This is where TDD comes in. As I mentioned in my No-nonsense TDD Booklet, TDD isn’t about test coverage or test-first. It’s about retaining testability and modularity within your codebase.
A deadline is a forcing function to keep your head clear of distractions.
TDD is a forcing function to keep your code clear of inappropriate coupling. This sets you up to win by combining cohesion (ie. high coupling within a use case) and Use-case driven integration tests, (unit tests) or BDD/Gherkin-style acceptance tests.
This will codify your behavior scenarios, giving you a high level confidence that:
it works in production
you can course-correct: transform or refactor when needed
Do you disagree? What detail would you have resistance to?