Friday, March 18, 2005

Increasing coverage and refactorability in legacy code

It's been over a month since I said I'd write more about Increasing coverage and refactorability for code with no unit tests, so here's my shot.

So here's the scenario: You're faced with trying to improve a large body of existing code with few to zero unit tests. The code itself is fairly monolithic, has a high degree of coupling, and maybe has an abuse of singletons, static registries or factories and too much "new" operator on large service components that make it really difficult to test a single class at a time.

To make matters worse, methods are large, the business logic isn't clear, the original requirements that drove the code (if any) aren't available. It's a tangled mess! Do you Have This (Anti-) Pattern?

So all I have is a hypothesis and some intuition, but here's the general sequence of actions that, if taken, may go some of the way toward solving the problem of increasing coverage and making it easier to refactor legacy code. The central tool in your toolbox for making this happen is none other than FIT.

What you're using FIT for in this case is for a higher-level testing tool to get some coverage for business cases that you may be able to discover, hopefully by finding and talking to a business expert. Using FIT allows you to automate the tests and get faster feedback than you would having to manually click through interfaces in your application.

  1. Start to write a FIT test page that covers an aspect of the code you're targeting for increased coverage.
  2. Start writing the FIT fixtures that will enable you to run the test. (If you're suffering from another anti-pattern of having too much application logic inside your MVC layer, you'll find that if you're writing action fixtures, during this stage you may have to duplicate large chunks of application logic that you have to cut and paste from models and/or controllers. Add another item to your list to clean up later.)
  3. Run the test(s).
  4. If possible, use a coverage tool such as Clover to see how much coverage you were able to buy with your tests. Now you have a concrete metric for what code you may be able to refactor.
  5. Refactor mercilessly on the code within the coverage zone, treading lightly on code that is not covered.
  6. Re-run the tests to make sure you haven't changed any behavior.
  7. Once you've refactored a reasonably small, unit-testable component out of the quagmire, write a unit test for it. You still want a unit test to cover your code in the long run as this will allow you to develop and improve the code even more quickly than you can with FIT.
  8. Repeat the entire cycle to spread out and cover other areas.

I haven't yet had an opportunity to put this to practice myself, but I'd be interested to hear if anyone else has tried something similar. If you have, drop a comment!

10:01:44 PM      comment []  trackback []