In the past (as an academic) I never wrote unit tests. I always ad hoc tested my code. Each function I wrote, I would run spot tests on from an interpreter and then build more functions on top of that, knowing the previous ones worked. This is the same as unit testing, but it is ad hoc unit testing.
Now, out of the ivory tower, I write actual unit tests. I do the same thing as before, except now the tests are written down in code and can be repeatedly called using a single command. And I do call them, several times a day each time I make a change.
This way is MUCH better than the ad hoc way and it TAKES EXACTLY THE SAME AMOUNT OF TIME and then SAVES TIME FOREVER AFTERWARDS.
Unit tests don’t mean EVERYTHING IS TESTED. A lot is left to your discretion as an experienced coder as to which functions are tricky and may have bugs and which functions are straightforward, or will reveal their buginess in downstream tests.
I think the hate for unit tests comes from folks who’ve been forced to write them by other folks who are not familiar with the code base or with coding.
The way in which tests act as a form of automation and “memory” is really quite critical to their value proposition. I don’t bother with 100% test coverage or anything like that, I just want some tests so things can be more easily understood and validated in an automated manner.
‘Course, I like types too - for similar reasons. :)
I’d also add that tests force you to explicitly stick to the spec and provide you with both immediate-term and long-term regression prevention. That’s a pretty amazing value prop.
I’m an advocate for TDD. But, it depends on the situation.
TDD is great at providing almost instantaneous feedback. When I first started programming (Java) I worked at a company where we had no tests, our codebase was set up in a way that did not accommodate tests, and it led to an extremely slow feedback loop. I remember times where I would make changes, spend many minutes rebuilding the project, testing it manually, and then hit a problem that I hadn’t anticipated and have to go through the whole thing again.
Later I moved on to another job, still Java but where things were TDD’d. It shortened the feedback loop by many multiples and provided a huge improvement in productivity. This is the “faster” that TDD proponents talk about.
More later, I moved away from Java into FP languages, and particularly REPL driven development. This is the ultimate feedback loop. If I’m ever unsure about something I can eval it and know right away. But, this led me to writing less tests. Right now I’m working on capturing my REPL experiments as tests, because…
TDD isn’t just about the feedback loop, the tests also leave “contracts” for future people working on the same code. When I encounter a large codebase that is covered in tests I have the confidence to go in and make changes, when there are no tests I don’t have that same confidence. Of course, passing tests doesn’t always equal everything-is-fine, but usually it does, and that’s a great thing.
About capturing REPL experiments as tests: I use https://github.com/darius/halp – it’s for Emacs and (mainly) Python. It’s actually a tighter feedback loop than a REPL gave me, plus persistent by default.
If you’re used to BDD, you stop appreciating how much better you’re developing until you see someone on your team try to do what you’re doing without it. It’s enlightening.