You might have heard that test-driven development, or “TDD”, is as simple as three easy steps. So without further ado, I present them here.
- Write a failing test.
- Delete that test. It was nonsense.
- Write a much smaller test with just an assertion and a couple of extra lines.
- Run the test and observe that the failure message is completely useless.
- Rewrite the assertion for good failure messages.
- Run the test again. Rejoice in your helpful failure message.
- Try to make it pass by implementing a new function in your test code.
- Realise that in order to make it pass, you have to basically duplicate the test setup, because you’re using stubs and mocks.
- Decide that this way lies madness. Rewrite the test a second time, without using mocks.
- Recognise that by not using mocks, you can’t inject an in-memory database, and so the test is going to be gargantuan and slow. You have to return a value that states the intent, and have some other thing actually do the database interaction.
- Make lunch.
- Eat lunch.
- Spend some time sketching out what it means to have a value that says “insert this thing into a table”.
- Come up with representations of state changes like
- Recognise that that’s a bad name, and instead investigate your domain language.
- Create structures to represent modifications to your application state in the language of your domain.
- Realise you have no word for whatever this “thing” is, and go ask someone.
- Have a long and fruitful discussion with your customer about what it really means when someone does this thing.
- Agree on terminology, and write it down.
- Add that terminology to your code.
- Write that assertion.
- Make it pass.
- Move the logic out of the test code.
- Tidy things up, move them around, generally let your brain relax as you clean things up.
- Make some tea. It’s been a long day.
You might already be familiar with a different set of steps. They look something like this:
- Write a failing test.
- Make the test pass.
- Refactor your code.
Also known as “red, green, refactor”.
While these steps definitely capture the method of TDD, they can make it sound easy. They explain the mechanics, but not the purpose. We use these steps to tease out misunderstandings, contradictions, and hidden complexity in our software and our (often informal) requirements. When they flow, and we can simply write the test, and make it pass, that’s a wonderful feeling.
But it’s when they don’t work that we’ve learned something useful about our software.