Write a test based on how you wish the object under test could be used. Then make it work. Don’t let yourself be limited by constraints and implementation details… nothing is holding you back! Just write it as you’d wish it to be.
This is a post in our series on Test Driven Development.
Writing the code you wish you had
By writing a test scenario guided by what we wish the code would look like, instead of the current state of the code, we can get feedback about design ideas without having to implement them first. This lets us to steer the design just in time, based on specific examples. It is a bit like sketching an interaction diagram on a very precise digital napkin.
We previously shared the “Think about design in test” heuristic. Thinking about design in test often involves wishful thinking, because we express our design ideas in the test before we make it work.
Wishful thinking in practice
Let’s return to the drinks vending machine test from the previous post. We want the vending machine to deliver a priced drink when we pay enough.
We wish for the machine to have a bin to catch drinks. We decide to inject the bin as a structural dependency. We can always change our minds and code later.
1
2
bin = Bin()
machine = VendingMachine(bin=bin)
To configure choices and prices, we fancy an internal DSL (Domain Specific
Language). In plain language: we’ll chain
function calls. This time we want to do money properly, with a Money class and a
euro
creation function.
1
2
3
machine.configure(Choice.Cola)\
.delivering(Can.Coke)\
.with_price(Money.euro(2))
Telling the machine to deliver
drops a drink in the bin. We get the contents of the bin using retrieve
.
1
2
3
machine.insert(Money.euro(2))
machine.deliver(Choice.Cola)
bin_contents = bin.retrieve()
Putting it all together:
1
2
3
4
5
6
7
8
def test_delivers_when_paid_enough():
bin = Bin()
machine = VendingMachine(bin=bin)
machine.configure(Choice.Cola).delivering(Can.Coke).with_price(Money.euro(2))
machine.insert(Money.euro(2))
machine.deliver(Choice.Cola)
bin_contents = bin.retrieve()
assert_that(bin_contents, equal_to(Can.Coke))
So far the code is only a wish in the test, it doesn’t exist yet. But it already gives us feedback about this approach. If we don’t like what we see, change is still very cheap.
When we write a test, we imagine the perfect interface for our operation. We are telling ourselves a story about how the operation will look from the outside. Our story won’t always come true, but it’s better to start from the best-possible application program interface (API) and work backward than to make things complicated, ugly, and “realistic” from the get-go.”
–Kent Beck, Test Driven Development, By Example, p. 4
Wishing for more
Sometimes we get stuck writing a test, because the new idea does not fit the existing code. By applying wishful thinking, we break ourselves loose from the constraints of the current solution (for now), allowing us to express how we imagine it to be. Once we have expressed our desired design in the test, we can evaluate it and see what it means for the existing code.
Whenever we get stuck in a design discussion with colleagues, wishful thinking can help in getting more clarity about the different design options. If we need to be more precise than just lines and boxes on the whiteboard, we can express our train of thought with wishful thinking and share it with our colleagues. It is a fast way to get feedback.
Behaviour Driven Development (BDD) with Cucumber is another way to do wishful thinking - we write scenarios in natural language, and postpone considerations about realising that wish in code (or otherwise).
Further reading
The practice of wishful thinking has been around for a while. An early instance can be found in: Abelson, Sussman & Sussman, Structure and Interpretation of Computer Programs
The Growing Object Oriented Software book mentions Write the Test That You’d Want to Read.
In his video series about Test Driven Development, Jim Shore calls it Programming by Intention:
- call the functions you wish you had
- comment it out
- implement the missing functions
- test, uncomment, fix up
Adrian Bolboacă has written a post about a coderetreat exercise based on the wishful thinking technique.
Learn more about BDD and formulating tests through the recently published book Formulation - Document examples with Given/When/Then by Seb Rose and Gáspár Nagy.
We use Interaction diagrams sparingly. If we do use them while writing tests we find the sequence diagram on a napkin or whiteboard useful. Especially with potentially complicated interactions or mock objects.
Elisabeth Hendrickson wrote a post about Wishful Thinking and Test Driven Development - The Long Way is Shorter
This is a post in our series on Test Driven Development.
Updated 13-07-2021: added a link to Elisabeth’s wishful thinking post