Stop worrying about test philosophy and start doing. Find a testing technique that works for you and make it into a habit.
If you’re new to testing, you might be convinced that it’s really scary. The hardcore Test-Driven Development (TDD) folks almost act like it’s a religion, but you’ve also got people who call TDD a “red flag.” Just outside the fray, “Poorly-written tests can actually be worse than no tests at all” is a bludgeon of perfectionism to keep you from diving in on either side.
On top of all that philosophical argument there are the deadlines. Impossible deadlines. How can you justify spending time testing when you have project managers breathing down your neck? Testing is easy to dismiss: The code is already working! You just wrote it and painstakingly verified it yourself! Ship it!
Did you catch that? You’re already testing! You already painstakingly verified your code!
How? Think about what you do now before you submit a pull request. If you’re a web developer, you make a change, then refresh the browser and click around in the app to verify it. For others, the easiest technique might be to verify behavior via live-coding in a read-eval-print loop (REPL). Or running a command-line tool and inspecting the output.
I like to think of automated testing as taking this effort and making it both repeatable and a little more rigorous.
Imagine trying to fix a tricky bug six months from now, deep inside the code you just wrote. A year? Two years? If you don’t plan to stay at your job that long, imagine someone new trying to make changes in your code. Tests can act as guide rails for future changes, reducing the probability of unintended consequences.
Ultimately, the tests you write are more about tomorrow than they are today, just like the code itself. But tests can still provide a lot of value as you write them. Your tests can catch scenarios you didn’t think about or handle perfectly - before you submit the pull request.
And they also can pull you forward into writing the feature in the first place…
As a full-stack developer, I write features which extend from the browser all the way into the database. Here’s an example of how I approach this kind of thing:
This seems like quite a bit more effort than the natural approach, right? Why not just make changes up and down the stack at the same time? Well, because I’ve noticed that when I do that I spend quite a bit of time in debugging mode. I’ve made changes X, Y, and Z but things aren’t working - which one is wrong? Maybe more than one?
I’ve converted that long tail of manual debugging into something that accrues long-term value for the project. Something that reduces worry when making changes.
This is a large example, but the same is true in the small for just about all code. There’s the initial development, and then the debugging process. Tests divide and conquer that process. You can feel confident that X works, then be sure that Y works, and so on - with no worry that your work to finish Y broke X.
Okay, so it is overwhelming. Even for me. When I think about all the tests required for an entire new feature, it’s too much. So the question I like to ask is: what’s the first barrier I can break through?
For me, in most cases, that very first barrier is to create a test file. For some reason it always feels harder than writing the actual product code. I have to scaffold it all up, then craft the right command-line test command to get the tests to run. If I’m really on the ball and the algorithm is a little more complex than usual, I’ll do all that and set up a
mocha --watch run to get a really tight feedback loop in place.
Once the test file is ready for the tests, it’s natural for me to brainstorm the set of tests that should be in it. Then I start writing them. And so it goes. One step at a time will get you there. The order of operations above is a natural outgrowth of the attitude ‘do the next natural thing.’
I gave my card to someone recently and they noticed the term TDD on the back. They immediately asked “do you really write all of your tests first?” I had to laugh. Definitely not. That’s not what TDD means. It’s a much more natural process than that.
Jump in! Do what feels right, using my process above as inspiration. You’ll be pleasantly surprised at how smoothly getting more and more detailed with the requirements blends into writing the tests. This is the true meaning of test-driven development. Detailed understanding of the tests helps you understand the problem more deeply, while also making the code required very clear.
Once you’ve had some of these experiences, you won’t want to code without tests. You’ll have a new, very helpful habit. You’ll have your test rhythm.
Now, with some good experience under your belt, you can even dip your toes into those fiery test philosophy debates once more!