This week's been aaaallll about automated testing for me. On Monday, I attended Preston Codejo, looking at unit testing in Ruby. I was there as a Ruby novice, there were a couple of TDD novices, so some great discussion around the merits of TDD. My working week has been spent sneaking SpecFlow and acceptance testing into a new project at work, and yesterday I spent a few hours watching one of Uncle Bob's Clean Coders videos with some colleagues. As usual, my DRL posse had some really interesting thoughts around it - more discussion around the merits of testing followed.
And then there's a bit of discussion on Twitter about TDD not being the silver bullet some would tout it, I speak very highly of TDD as a practice. Some would describe me as a bit of a zealot, so in the interest of balance, I want to make a confession.
I Don't Always Test
This is a confession that won't shock some people, but I do have a couple of peers who would suggest I burn in hell for saying this.
I don't always test my code!
Now hold on - let's not carried away with that statement, let's not have any quotes pop up on twitter taken out of context (@datoon, I'm looking at you!). I'm really talking about a tiny, tiny percentage of edge cases. Maybe 1 in 1,000 times I create a new class I'll knowingly not test it. Specifically:
I won't test my code if I'm certain I won't get the return on my investment.
Return On Investment!?
Yeah, return on investment. Writing tests isn't free, y'know - even the most dogmatic of testers won't try to fool you into thinking that it's free to get started. When you write code test-first, those first few baby steps involve a lot of stopping and starting. I like how @UncleBob describes this as "Playing some kind of weird game with yourself", it does feel a lot like that.
So why do we play this wierd game, what's the point? To me, there are three main points:
- Producing an elegant, decoupled design to a solution - that is if there is a problem needing a solution. Sometimes you're just wiring up stuff that a moron could manage.
- Allowing bug-free flex once the project has reached a level of complexity where the whole thing won't fit in my head at once - that is if the project ever gets to that level.
- Maintaining an agreed clean code standard where I'm working with other developers. (This one's essential - I will ALWAYS TDD with other devs)
So what do we do when we come to a project that contains no problems, and will get thrown away the same day as a hack? This project wouldn't need to be elegant, and it wouldn't need to scale.
Right, let's look at some examples where in the past I've not TDD'd.
- Playing with new tech - If I'm creating a little app to test a new piece of tech, for example playing with RavenDB, or NancyFX, I wouldn't TDD it.
- Hacking - I've got in my mind a few little apps that I'll create for my own interest, that are simple little hacks. For example they might scrape a website and throw the data into a CSV file. I wouldn't be proud of the code, but I'd be less proud of TDDing a hack-app when my open-source stuff needs to much TLC.
- Coding Contests - Specifically, short (less than 2 hour) coding contests where the aim is to create a bot. (Hmm... that's really specific) This one's from personal experience - creating a bot to play tic-tac-toe. I watched the other teams TDD out a nice soluton, we had a giant ugly if statement. We won. (I felt a bit bah humbug after this, but hell, we won pretty decisively. ;-)
Don't You Just Feel... Dirty?
I've been cutting code now for 24 years, I've been TDD'ing for just under 2 of those years. I've been religiously TDD'ing for just 1 solitary year. After just that short 24th of my coding career, I've now become a keen promoter of TDD, having seen that in 99% of cases code that's created test-first is just plain better than the same code written by the same people non-test-first.
But pragmatism over idealism, I don't feel guilty when I don't TDD. I just make darn sure I only drop the tests when I'm 100% sure it's the right time to do it.
Some Counter Arguments
Okay, there are of course some counter arguments (I'd love to hear some more).
The first one is around practice, practice, practice. Some devs consider that every line of code you cut is practice that should be directly applicable to your day job. If you deliver a hack job in an hour instead of 2, you've saved an hour, but lost two hours of TDD practice. Myself, if my day job involves 30 hours TDD a week, and my open source work is another 10 hours TDD a week, I can forgive myself that.
And then some folks will argue that it's impossible to predict applications that fit my criteria to non-TDD. How do you know a hack won't become a fully fledged app, in which case boy will you regret not writing them first tests! What happens if someone wants to look over your ugly non-TDDd code, could be kinda embarrassing? They're pretty valid points I guess, maybe they're just yet to happen to me.