Phil Haack has a great post up in response to some folks unhappy with their unit testing experience. I think he makes several good points, including:
- Unit testing is only one layer of testing (duh)
- Automated unit tests are much, much simpler to write when you’re writing the tests upfront and purposely creating testability
Like many people in the blog trail is saying, there are some types of code that are just too difficult for automatic tests. User interface code is often an example, both heavy clients and web applications. Testing the actual presentation code is difficult (but certainly not impossible). Before you throw the baby out with the bath water and abandon TDD altogether for UI projects to rush in new features, consider this:
Jeremy’s First Rule of TDD – “Isolate the ugly stuff “
Take the things that are truly difficult to test automatically and wrap them behind abstracted interfaces and separate any other code and functionality away from the difficult to test code. Typically I think this would be access to external systems, user interface windowing, or anything involving a web server. A picture perfect vision of my Grandmother boiling turkey bones after Thanksgiving comes to mind. The bones aren’t very edible, but you can do something to get the rest of the meat off the bones for endless post-Thanksgiving sandwiches.
Circling back to user interfaces, the Model View Presenter pattern is a perfect example. Actual user presentation code (System.Web.UI and WinForms) is harder to test, so make that code smaller. Slice the “ugly” View classes as thin as possible. Move the user interface flow logic into “plain old object” classes (Presenter) where it’s much easier to test. It probably is more code, but it’ll make the UI code much easier to test and therefore easier to make work. Since UI flow control code can become very complex and scary to change, it’s awfully nice to be backed up with the automated unit tests. You might decide to forgo unit testing the actual UI view code and call it a tolerable risk, but at least the controller logic will be unit tested.
The other example that comes to mind is a system I did last year that had some custom Active Directory queries in the authorization code. We punted pretty early on trying to write automated unit tests for the actual Active Directory access code. Instead, we put the AD access code into a thin Gateway class and interface so we could test the rest of the authorization logic without stepping into the Active Directory queries. The only place that had any actual interaction with AD was the Gateway class itself.
I would say that a lot of doing TDD is learning how to divide the class responsibilities up in a way to make the code easier to test (3 years so far and counting). The fact that you often write code differently just for easy unit testing doesn’t bother me a whole lot because it contributes to finishing the code sooner. You can’t just write code without regard to testability and expect to be able to retrofit unit tests on later. I made the classic newbie mistake of trying to just write the code, then write the unit tests later only to find that my code didn’t allow for easy unit tests. TDD goes a lot smoother as soon as you realize that you need to change the way you build and structure code to take advantage of TDD. Learning to write the unit tests first helps a tremendous amount too. I think the book on best TDD practices is still being written, but there’s a lot of experience out there already. Before you throw your hands up in frustration over TDD, take a look at how other people are writing code for testability.
P.S. – I don’t know about Swing, but you can efficiently TDD a great deal of WinForms client code. Maybe not everything is worth an automated unit test, but anything you can do helps. Even without using something like NUnitForms a lot of WinForms classes can be treated as just “POO” and tested in NUnit/MbUnit without too much hassle.