Why aren't tests as composable as production code?
If test code were just explicitly wired dependencies in local variables, composition would be straightforward.
But once you introduce asynchronous processing, messaging, or higher-level test frameworks, what the test is actually doing becomes opaque.
- Execution order becomes implicit.
- Data flow becomes hidden.
- Coordination turns into guesswork.
As a result, reuse breaks down and copy-pasting test logic becomes the path of least resistance.
Not because developers prefer it — but because composition is no longer obvious.
Explicit test steps
To make tests composable again, test steps need to be defined in plain sight:
- explicit dependencies
- visible data flow
- minimal surface area
Only then does composition become something you can reason about.
Plain objects
One way to achieve this is to treat integrations as plain objects.
In Xcepto, adapters are instantiated as local variables. They behave like any other dependency you would wire in production code.
Each interaction is defined explicitly — whether it's triggering behavior, observing state, or waiting for a condition.
That keeps:
- dependencies visible
- data flow explicit
- coordination driven by conditions, not time
The result is a test structure that mirrors how systems actually interact.
Originally published on themassiveone.net.
