views:

665

answers:

7

Do you have any strategies for retrofitting unit tests onto a code base that currently has no unit tests ?

+10  A: 

Read Working Effectively With Legacy Code by Feathers.

Jimmy Bogard has a good blog series on SOC.

Matt Hinze
+1  A: 

Here's another great article on testing. In particular, a somewhat relevant quote from it:

Here’s a terrible idea - decide you are going to spend a whole week building a test suite for your project. First of all, you’ll likely just get frustrated and burn out on testing. Secondly, you’ll probably write bad tests at first, so even if you get a bunch of tests written, you’re going to need to go back and rewrite them one you figure out how slow, brittle, or unreadable they are.

I think you are better off building tests 1 at a time as you are fixing bugs or adding new functionality... don't try to build missing test cases, you should have an end goal for each test, rather than just to improve coverage.

Mike Stone
+5  A: 

The best way to retrofit an existing project without any unit tests is to do it when fixing bugs. Write a test that fails on the logic that has the bug in it with the steps to reproduce the bug. Then refactor the code until the tests pass. Now you can have confidence that the bug is fixed and it will not be introduced later on in the cycle and you started introducing unit tests into the project.

Dale Ragan
A: 

Dale gets voted up. Yes, there is no gain for adding unit tests to code that's working. Lets say there are two unknown bugs X & Y. At some point X is revealed by typical field use. You fix it, add a unit test, and move on. Now lets assume Y is never uncovered over the entire lifetime of the program. Since Y never revealed itself it's as if it never existed; no need to waste the resources. Multiply this by hundreds or thousands of dormant bugs and you save yourself a great deal of superfluous maintenance.

xanadont
Re: "...there is no gain for adding unit tests to code that's working." How about the gain of: "Now if changes are made to my code, I have a better chance at detecting side effects (bugs) introduced by my changes." ?
JeffH
As soon as you add that change you need to add a test(s) that exercises whatever code the change affects. Code analysis tools will reveal this. Also, human testers will reveal side-effects. When these are revealed they're considered bugs and then my above comment applies. Do so recursively.
xanadont
+1  A: 

If ever you are trying to add unit tests to old perl code I strongly recommend

Perl Testing: A Developer's Notebook by Ian Langworth and chromatic.

It has some very nice trick on testing legacy and "untestable" code.

Vagnerr
+1  A: 

Why do you want to add unit tests? Do you feel the code has bugs? Do you just want something to do? Are you about to embark on a new feature?

If it is an older product that has been released for quite some time then I'd agree with the others and only add the tests when I find a bug or add a new feature.

If it is a product that is still being developed and not released or only recently released, then I'd start by reviewing the code. If I saw something not quite right then I'd add a test for it. I'd probably make some tests to create some sample data. Creating sample data seems to offer quite a bang for your buck, and it can be useful too.

I think there is benefit to writing the tests even when you don't have a bug to test - when you're adding new features or fixing bugs later, your tests confirm that you haven't introduced new bugs.

dan gibson
A: 

Is it possible that we are in a panic and are getting confused between unit tests and performance tests? Is it that your application works fine with few users, but starts throwing errors when under heavier load? If so, unit tests are not the answer. Unit tests != Load tests.

If unit tests are in fact the answer, retrofitting unit tests is a good idea as it will help clean up the code. Just be prepared to refactor a lot. Code written with TDD turns out looking a lot different than code written without TDD. In my case, I had a method HandleDisposition() which took care of a lot of cases. This kind of method would not have existed if we had written the code with TDD. When retrofitting unit tests, we refactored that function and now have methods like XDisposition(), YDisposition(), ZDisposition(), which are a lot easier to write unit tests against.

kinjal