In some sense, I grew up doing TDD - test-driven development.
At my first internship we were doing TDD. I was taught to red-green-refactor. We often paired, one person first wrote a test and the other one then had to write the code to make it pass, then we flipped the roles. We were programming in Ruby and at the time TDD was the way to write code in Ruby. I prepared for that internship by working through railstutorial.org, which also shows the failing tests first.
There was a life before tests, though. I didn’t fall into a well full of tests as kid. The first program I ever wrote wasn’t a failing test, it was Hello World. I started programming as a teenager and didn’t learn about tests until a decade later.
And yet I can’t imagine what it’s like to program without tests anymore. More than a couple hundred lines without some tests? Sounds uncomfortable. It’s not about correctness and coverage, though. Not really. Well, not only that.
Having tests is having code that I can quickly run, ideally with a couple of keystrokes, to exercises other code that I wrote. It’s about feedback, right there, in my editor. With tests, I don’t have to run this, wait, click this, run this, take this and put it in this – I can just hit ,rt
in Neovim and see whether it works. Ideally, of course.
What a REPL and a debugger are to others, that’s what tests are to me. Here I can play around with an idea using all the tooling my editor gives me in a normal file. I can zoom in on a bug by writing ever more granular tests and isolating the faulty part step by step – all comitted and recorded. Sometimes I write failing tests for missing features or for nasty bugs and keep them around, commented out, for weeks, until I have time to make them pass. Correctness and preventing regression – cherries on top. A test suite that runs in less than 1s is worth having just for the joy it brings every time you run it.
Tests have their downsides too, of course. They can make changes harder because now you have to change the system and its tests. They can be ghosts from the past that whisper “it must be so” in your ear when you’re not sure whether you can change something or not; echos of the programmer who wrote them years ago and left. They can become slow. They can be useless, dead weight when they don’t test anything. Acknowledged, signed, yes, tests can be bad.
I just don’t know how others can live without them.
> Sometimes I write failing tests for missing features or for nasty bugs and keep them around, commented out
It was only with LLVM's LIT that I learned about XFAILed tests. Really neat way to specify missing or incomplete features. TDD can also profit from it, because the test case can be committed as NFC in insolation upfront and the feature implementation just unXFAILs it, like:
https://github.com/open-obfuscator/o-mvll/pull/16/commits/ae1ef8cb337b156941dbe709e7597f28dd5d8b67
I sold a SaaS company doing $35M in annual revenue with zero tests and no QA team.
Trust me, you can survive without them. 😁