Given-When-Then is not just syntax. It’s a mindset.
For years, system tests have been written with Arrange–Act–Assert.
That works — until asynchronous behavior enters the picture.
AAA implicitly assumes synchronicity:
- Arrange
- Act
- Assert
The assertion happens after the action, as if the system has already settled.
But distributed systems don’t behave like that.
- An API call may publish an event
- A queue consumer may process it later
- A workflow may eventually converge
Timing becomes unpredictable.
The common workaround?
- Sleep(5000)
- Retries
- Polling loops
Fragile tests are built on timing assumptions.
Waiting for causality
The real problem is this:
We wait for time instead of waiting for causality.
In asynchronous systems, timing should be causal rather than actual.
Instead of saying:
"Wait 5 seconds, then assert"
You say:
"Wait until this condition becomes true, then continue"
That is the real power behind Given–When–Then.
- Given a scenario
- When a condition becomes true
- Then validate the resulting system state
And once you think about testing causally, an interesting property emerges:
You can chain multiple When -> Then transitions together.
Each validated state becomes the next Given.
Xcepto.NET
This is exactly the execution model behind Xcepto.NET — my open-source system testing framework.
Tests are compiled into condition-driven state machines where transitions happen because conditions are satisfied, not because enough time passed.
- No manual sleeps
- No retry loop
- No pretending distributed systems are synchronous