Unit Testing for Existing Software

Only very few teams have to start a new software development project from scratch. A successful project is normally in the maintenance mode most of its life span. If we want to introduce a new technique such as unit tests or even test-first development to such a project we will have to deal with a few difficulties:

So no wonder Kent Beck [00a] considers testing one of the most frustrating areas for a development team that migrates to XP. Beck recommends resisting the temptation to create subsequent unit tests for all existing code. Depending on the project size, this would mean that the team would be tied down for weeks or even months without delivering additional functionality for the customer. Another approach is more promising and above all less risky: while new system parts are totally subject to a test-first development, the test cases for existing classes are added one by one as needed:

If you follow these recommendations, the first thing you will notice is that the development speed will slow down. However, after some time, the testing coverage will achieve a satisfactory result even with the old code base, and the differences between old and new code are getting smaller. Another positive effect is that exactly those parts of the program that are run or changed often are those heavily tested.

Testing around Legacy Code

As plausible as this step-by-step approach may sound, it has one big draw-back: writing unit tests around legacy code is as easy as creating tests before the implementation only in rare cases. Often we find the old code base is so bad that only concurrent refactoring enables us to create unit tests at all. Michael Feathers [02a] discusses the issue and some techniques to tackle it in Working Effectively with Legacy Code. It is, however, impossible to formulate a universal recipe for how to solve the problem in a specific case. Most of these cases require a sound mixture of caution and instinct to secure the most problematic parts of legacy code by test cases and to make them accessible to refactoring or to a complete replacement. Nevertheless, there are a few proven heuristics: [1]

Creating subsequent unit tests is a tricky and challenging business, mainly due to the fact that testability was not a criteria in the design of that code. This means in some cases that enormous efforts are required to turn existing software into a permanently maintainable condition. In the worst case, frustration about later testing causes the entire testing approach to collapse. These risks and investments should be weighed against the cost of a complete or partial new development. More often than initially expected, a redevelopment from scratch turns out to be the cheaper variant.

A team that is nevertheless entrusted with the task of maintaining "testless" software should mainly avoid one thing: simply getting straight down and going without first building a reassuring net of test cases. If the customer remains stubborn, insisting on "quick changes," then there is still the option of just not telling them about these indispensable quality measures. [1]The following summary corresponds essentially to discussions at [URL:YahooXP] and [URL:WikiUTFLC].