“I’ve always kind of hacked my code together and had the attitude like testing was for chumps.”
That was posted on Reddit the other day by someone, but it could easily have come out of my mouth a few years ago. This post is written for anyone who identifies with that statement and is wondering why they should bother with TDD.
My thesis is that you’re already doing TDD, you just don’t realise it. Because you don’t realise it, you’re not using any of the tooling that exists for it, so your life is harder than it needs to be.
When you write some code, I’m guessing your process looks a lot like:
- Think a bit about what the feature needs to do.
- Write some code.
- Run the code to make sure it did what you expected it to.
- Repeat steps 2 and 3 until it’s correct.
- Move on to the next thing.
When I write some code, my process looks like:
- Think a bit about what the feature needs to do. 1b. Write those thoughts as some code that will tell me when my code does what I expect it to do.
- Write some code.
- Run my helper code to see whether my code does what I expected it to.
- Repeat steps 2 and 3 until it’s correct.
- Move on to the next thing.
So, they’re pretty much the same, right? Only my process has a whole extra step! How inefficient! In practice, though, not so much.
The core is that your step 3 is really hard. How do you do it? Maybe you insert some debug logging to see whether some key variables are within an expected range. Maybe you look at the end user output to see whether it’s what you expected it to be. Maybe you step through your code with breakpoints in a debugger. To do this, you probably need to load up the interface and push some buttons. You probably needed to enter some information to create the state you need to exercise your code. It’s probably a pretty manual, labour intensive process.
My step 3 is really easy! I just run the code I wrote in 1b. “But didn’t it take forever to write all that code?” I hear you cry. Not that much, really. You get faster at it with practice.
It’s like any automation task. It takes a little bit of time up front, then you save time every time you need to do the thing. You automate all kinds of stuff already, right? You use a programming language that automates turning you instructions into assembly code. You use an editor/IDE that’s faster than a pen and paper. You use makefiles/build scripts to build your code and manage assets. You use a package manager to manage dependencies. A testing framework is just another one of these things.
Convinced? Great! Don’t get too hung up on ‘TDD’ the way the internet says you should be doing it. Just write some tests before you write some code. You can figure out where you stand on the religious issues later.
Now that you’re a convert based on the above, you also get all sorts of other great stuff! Remember when we wrote down our thoughts about what the feature should do? That’s documentation! You have a spec!
And we don’t throw those tests away now that we’re done with them. The next time we write another feature, we run them again! That way we can be sure the new feature didn’t break anything that worked before!
Also, when someone else wants to change the way your feature works, they don’t need to wonder what your code is meant to do. They can read and run the tests! Science!