Adding unit tests to legacy code

24,738

Solution 1

The best way, I have found, is to incrementally add the unit tests, not to just jump in and say we will now unit test the application.

So, if you are going to touch the code, for bug fixes or refactoring, then first write the unit tests. For bugs unit tests will help prove where the problem is, as you can duplicate it.

If refactoring, you will want to write unit tests, but you may find that the test is impossible to write, so you may need to find a high level, that calls the function that will be refactored, and unit test that part. Then, as you refactor the offensive function, write your tests so you can ensure that it is operating as it should.

There is no easy way to do this.

This question may help with more suggestions. How do you introduce unit testing into a large, legacy (C/C++) codebase?

Solution 2

Michael Feathers book "Working Effectively with Legacy Code" is an entire book covering this topic. Michael states that it is often too difficult to introduce tests for legacy code because it is not structured to be testable. What I got out of the book the most was a couple of patterns named "Sprout functions" and "Sprout classes". A sprout function is one that encapsulates the change that you need to make in the code. You then unit test these functions only. The sprout class is the same idea except the new functionality is contained in a class.

Solution 3

Yes, and it's generally painful. I've often ended up having to write integration tests instead.

The book The Art of Unit Testing has some good advice on this. It also recommends the book Working Effectively with Legacy Code; I haven't read the latter yet, but it's on my stack.

EDIT: But yes, even minimal code coverage was worthwhile. It gave me confidence and a safety net for refactoring the code.

EDIT: I did read Working Effectively with Legacy Code, and it's excellent.

Solution 4

Look also at the new approach in the area of legacy code unit testing - Asis project, it is inspired by ApprovalTests project and shares its key concepts.

As mentioned about ApprovalTests approach in this article:

Often you have a huge legacy code project where you have no tests at all, but you have to change code to implement a new feature, or refactor. The interesting thing about legacy code is - It works! It works for years, no matter how it is written. And this is a very great advantage of that code. With approvals, with only one test you can get all possible outputs (HTML, XML, JSON, SQL or whatever output it could be) and approve, because you know - it works! After you have complete such a test and approved the result, you are really much safer with a refactoring, since now you "locked down" all existing behavior.

Asis tool is exactly about mantaining the legacy code through creating and running characterization tests automatically.

For further information look at

Solution 5

Take a look at the free, open source unit-testing utility library, ApprovalTests. If you are a .NET developer, the creator, Llewellyn Falco, has made a series of videos showing how he uses ApprovalTests to improve unit testing for both new and legacy code.

Share:
24,738

Related videos on Youtube

BuckeyeSoftwareGuy
Author by

BuckeyeSoftwareGuy

Updated on July 05, 2022

Comments

  • BuckeyeSoftwareGuy
    BuckeyeSoftwareGuy almost 2 years

    Have you ever added unit tests, after the fact, to legacy code? How complicated was code and how difficult to stub and mock everything? Was the end result worthwhile?

    • BuckeyeSoftwareGuy
      BuckeyeSoftwareGuy over 14 years
      I'll check out "Working Effectively with Legacy Code". Hopefully it'll give me some good pointers on how to write wrappers for all these static dependencies!
    • LCJ
      LCJ over 10 years
  • user6170001
    user6170001 over 14 years
    +1 for "Working Effectively with Legacy Code": full of great advice; in fact it's well worth reading even for greenfield environments, just as a great resource on constructing code for testability.
  • TrueWill
    TrueWill over 14 years
    +1 for incrementally adding the tests.
  • DVK
    DVK over 14 years
    +1 for idea of replacing unit tests w/ integration tests. With proper mocking, the former are Good Enough, quite often
  • lolololol ol
    lolololol ol about 7 years
    How does this not have more upvotes? If the repo does what it claims it does, this should be the selected answer.
  • lolololol ol
    lolololol ol about 7 years
    Does it deal with side effects in functions, btw? Even possible to solve this problem?
  • Vin Shahrdar
    Vin Shahrdar over 6 years
    I started creating unit tests for the legacy code that I have been working on. However, I realized that I have been writing integration tests, not unit tests because I create actual data and I insert them into the database. I don't see a way to create pure mocks and stubs for this legacy code because it is not structured for testing at all.