Monday, July 08, 2013

Thoughts on unit testing

comment on Programmer's StackExchange has started me thinking again about a question I posted a few years ago on the topic of unit testing.  The responses I received surprised me by how strongly and unyieldingly they were in favor of unit testing, some to the point of being offensive.  Looking back I can now see why that happened.

At the time I posted the question I was concerned I was missing out on an important aspect of being a developer.  I heard lots of talk about the importance of unit testing and the promise that test-driven development would take my code to an otherwise unachievable level of quality.

One cannot argue with the benefits of unit tests.  The safety net against regression. The documentation of intended behavior.  The method of fixing a bug by first adding a unit test to expose the bug, and then seeing the unit test turn green when you've fixed it.  How better to stamp out a bug?!  Who wouldn't want to have those assurances about their code?

But at the end of the day, I've always put one thing above all else: Getting the job done.  

My job at the time was writing line-of-business apps for a small company.  I was one of only a few developers and my customers were literally down the hall.  I didn't "ship" anything.  But I delivered lots of software to my fellow employees and often responded very quickly to add new features or new applications that would help them do their jobs.  Speed was crucial and my software was on a constant release cycle.  It is not the model scenario that is often highlighted in software development articles and books.

I had heard a lot of talk about trying to reach 100% test coverage and following TDD's write-tests-first-then-code approach (the red-green-repeat method.)  With the work I was doing those ideals just didn't make sense.  My gut told me so, but I went looking for validation anyway.

I asked "when is it appropriate to not unit test", and in hindsight that was too vague.  In my mind I was thinking along the lines of when is it appropriate to not fully invest in unit tests to the degree that TDD recommends or to worry about 100% test coverage.  I drew a line in the sand as if it were all or nothing, and naturally I sparked many heated responses. Furthermore, my question led to a blurred line between TDD and unit testing, which only led to more confusion.

The resounding answer was that of course I should unit test, and unit test everything.  Even Uncle Bob Martin chimed in with an all-or-nothing response detailing the benefits of TDD and 100% test coverage. He's absolutely right about the benefits, but his answer, and others in the same vein, misses the mark.  It is not black and white.  There's a lot of gray area here and many factors to consider, and there isn't one right answer to this question that fits all situations.

What did I take from all this? 

After working for a few years on a shipping product, my opinion is that we all should make unit tests part of our developer's toolbox. If you aren't doing it now, try it out. Use it in a real project. Get a test project setup, get used to running it regularly, and start by writing a few tests against an easily testable part of your code.  Get familiar with how they work.

Then see where things lead from there.  Erase this notion that you need 100% test coverage.  Don't worry about writing tests first.  Start small.  Maybe you get 10% coverage, but it's a heavily used 10% that is called from multiple clients.  Maybe you get really into testing and you get 80% test coverage but that last 20% is too difficult to make testable.  It's OK.

The best response I received, and good advice I'd give to anyone else is "...You don't have to test everything. Just the relevant things." from S.Lott. That woke me up to the idea that this isn't a plug-and-play solution and you have to decide for yourself how much to do.

Also, give TDD a try for a week.  Make yourself think about what you are writing to the point where you can write a unit test against it, and then write the code.  That's a great exercise even if you don't completely buy into TDD.  If it leads you to follow TDD regularly, great!  Don't feel bad if it doesn't work for you.

Keep in mind why you are doing it -- you're trading time now for better quality and stability in the long run. Whether you'll reap the benefits of that depends on what you are working on.

If you work with a lot of UI code you may come to the same conclusion as me, which is that there's usually too much friction to make this useful. So I don't write unit tests against that code. Instead I focused on writing them in a different layer where my application talks to a REST API.  It was a natural fit for unit testing, and it has simplified bug fixing and given me peace of mind that that code won't break in the future.