Matt Gallagher, on his absolutely fantastic Cocoa with Love blog, recently wrote a series of posts about test driven development (TDD) and unit testing Mac and iPhone applications:
- A sample Mac application with complete unit tests
- A sample iPhone application with complete unit tests
- Quality control in application development without unit testing
However, I heartily disagree with a major point in the conclusion of his final post:
Unit testing an application is filled with difficulties and problems. In my development style, I consider the time cost of unit testing an application outweighs its benefits — especially since a unit tested application still requires system tests like user-interface and regression tests for proper validation.
Before I talk about why I disagree, I want to say that I do agree with the next part of the conclusion:
Regardless of whether you use unit tests, formalized system testing — either automated or manual and methodical — is required to fully validate an application and ensure the lowest possible low bug rates.
Unit testing, while practicing TDD, is not about testing, in the traditional sense. Unit testing does not tell you that your final application is actually easy to use by the end user. Unit testing does not tell you that you actually built an application that solves the problems you intended to address. Unit testing does not tell you if all your units work together once wired up. That’s what higher level acceptance and system tests are for. Acceptance and system tests are required to ensure the overall quality of your application.
However, this doesn’t make unit testing worthless. Applications have two kinds of quality: external and internal. External quality is how the end users perceive the quality of the application: does it crash, is it easy to use, etc. Acceptance and system tests keep the external quality high. Internal quality is how the developers perceive the quality of the code: is easy to understand and maintain, etc. TDD and unit tests are about keeping the internal quality of the application high. High internal quality makes bugs easier to find and features easier to implement. Unit tests provide an essential safety net to be able to refactor your code, improve the design, and avoid code rot. Ultimately this makes software cheaper (and more fun!) to write and easier to stay on schedule.
Thus any real world application should have both unit tests and acceptance tests. They are not mutually exclusive and they do not serve the same purpose. I honestly believe that if writing unit tests for TDD is slowing you down, then you are not properly practicing TDD. Remember TDD is a learned skill, and like any learned skill, it’s not something you pick up overnight. It takes practice. Is TDD a panacea? No, it’s not going to cure cancer and bring world peace, but I think it is the best tool we currently have in our arsenal to keep code clean, flexible, and maintainable.