EDIT: What I should really say is that it isnt’ just Designing for Testability, it’s Designing with Testability

From a question on my Passive View blog post:

“should we design for testability, or should we try and test what’s designed (perhaps designed badly, so we refactor later)?”

Here’s my take:

“Done, done, done” isn’t just writing code. It’s writing code and verifying that that code works correctly. You don’t ship until the code is proven to work (hopefully). Designing for testability might cost you extra time in coding (which I would actually dispute somewhat), but can easily save time in the whole by cutting down on the time spent debugging and testing. I see testability design as a way to optimize the time to deliver, even if it ups the time spent on design or coding.

One of the very painful truths that TDD newbies learn the hard way (myself included) is that retrofitting automated tests to existing code can be very difficult. Just trying to test what’s designed may not work out very well, and frankly, I have yet to see a codebase that wasn’t built with TDD that was easy to test.

What is Testability design anyway? Granted, there are some things I do like opening up more public accessors or pulling out more interfaces strictly for testing that could arguably described as “bad.” However, most of what constitutes designing for testability, or using testability as a design heuristic, is a matter of how best to assign responsibilities by following older design principles that predate TDD by many years. Achieving testability is mostly a matter of separation of concerns, coupling between classes and subsystems, and cohesion. Exactly the design qualities that we’ve always strived for to make our code maintainable. It’s what we’ve been trying to do anyway. If you practice traditionally good design, you might already be there to testability.

Testability is, in my opinion, the ultimate design smell detector at the granular level. If you’re finding it hard to unit test your code, you’ve likely got a coupling or cohesion problem. Testability is yet one more design tool to stick in your design toolbox right between UML/CRC modeling and code smells. At least think of it this way, driving an application design at least partially through tests is yet another example of starting with the end in mind. How will I know that this code works correctly? How will I know that I’m done?

Yes, using TypeMock or switching to a dynamic typed language will let you more readily create testing seams with less conscious effort, but that’s not the entire ballgame. Throw testability and orthogonality out the window to write a ball of mud and no amount of TypeMock magic is going to help you out.

Anyway, go ahead and start arguing with me. As always, comments are open.