Possible Duplicate:
Best practices for retrofitting legacy code with automated tests

I've been working on a project in Flex for three years now without unit testing. The simple reason for that is the fact that I just didn't realize the importance of unit testing when being at the beginning of studies at university. Now my attitude towards testing changed completely and therefore I want to introduce it to the existing project (about 20000LOC).

In order to do it, there are two approaches to choose from:

1) Discard the existing codebase and start from scratch with TDD
2) Write the tests and try to make them pass by changing the existing code

Well, I would appreciate not having to write everything from scratch but I think by doing this, the design would be much better.

What would be your approach?

  • Writing from scratch is a very critical measure, so if you have a change to avoid it, keep working on the existing codebase – superM Sep 28 '12 at 8:52

Rewriting from scratch just so you can have automated tests is silly - you have a codebase that (mostly) works, and you probably can test it (just not automatically). A rewrite, even with all the tests in the world, introduces extra risk, and it always takes longer than expected. So don't do that.

Writing tests to achieve 100% coverage in one go is also silly, because it means you are halting on-going development to implement something which doesn't add any value yet. In most situations, this is unacceptable. Further, writing tests for code that already works and doesn't need changing has little benefit other than verifying that it does indeed work (but if it's running in production, you better be pretty sure about that already).

The best way to go, IMO, is to add tests as you go. That is, for every change you make, apply the following steps:

  1. See whether existing tests sufficiently describe the current functionality.
  2. If necessary, add tests to capture the current functionality of that particular part / module / class / function / ... Verify that they pass.
  3. Refactor existing code if necessary.
  4. Modify the tests to reflect the intended new behavior.
  5. Modify the code to make the tests pass.
  6. Refactor.

Steps 4 through 6 are just basic TDD; the only addition is that you retroactively add tests as needed before you start the actual TDD cycle.

If you follow this procedure, and the tests you add in step 2 are sufficient, the codebase will gradually move towards full test coverage.

Of course, if you are going to rewrite anyway, for different reasons, then going TDD right from the start is probably a good idea.

  • The reason for prossible rewriting is that after I leave university the project should be developped further by another student and I fear that he won't get along with the "mess" I'll leave behind – McMannus Sep 28 '12 at 10:07

Legacy code is working code, it has been tested in the wild. The worst thing you can do it throw it away and replace it with new code. Even with the best new code (with TTD ect) will have occasional bugs.

What I would recommend it every time you hit a bug you create a unit test around that functionality. Its not much extra work when your fixing things anyway and will mean the more buggy areas will get unit tests first.


Well, I would appreciate not having to write everything from scratch but I think by doing this, the design would be much better.

This is a very wrong assumption. Testing has nothing to do with design. You need to understand the benefits of TDD in more details.

Ideally if you have a stable working product which do not require any change, test cases will not help you. Automated tests only help you with re-factoring or feature addition.

For an existing project, the best way is to analyse the change (re-factoring / feature / bug-fix) and then write test cases for only that code.

EDIT: I do no accept the fact that testing impacts. but even when it does, we have the problem of ends-means reversal. Design should be bases on problem domain, and not on testing . TDD is just a means to achieve the end, which in this case is solving the problem in hand.

  • 1
    Code not having tests is not necessarily (and usually not) designed for testability, so testing does have something to do with design. Some architectures are nigh-impossible to do any unit-testing in. – Macke Sep 28 '12 at 9:29
  • there is nothing like high-impossible, it is just that it is not easy. And even then unit testing is easy, the problem comes with integration tests only... – Apeirogon Prime Sep 28 '12 at 10:25
  • 2
    @apeirogon: I agree that the impact of TDD on the overall design is small, thus +1. But I also agree to Macke that TDD has an impact on the design aspect "testability". What I disagree is that unit testing is easier than integration testing. In legacy code bases you often cannot unit test since there are not small units, the code is seldom decoupled. But you often can do integration testing, since most programs have interfaces "to the rest of the world", which can be naturally used as an anchor point for tests. – Doc Brown Sep 28 '12 at 10:56

Not the answer you're looking for? Browse other questions tagged or ask your own question.