Adding unit tests to legacy code

Unit TestingLegacy Code

Unit Testing Problem Overview


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?

Unit Testing Solutions


Solution 1 - Unit Testing

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. https://stackoverflow.com/questions/748503/how-do-you-introduce-unit-testing-into-a-large-legacy-c-c-codebase

Solution 2 - Unit Testing

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 - Unit Testing

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 - Unit Testing

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 - Unit Testing

If you are planning on refactoring the legacy code then creating those unit tests is a must. Don't worry about mocking or stubbing - worry about testing the inputs and outputs of the system so that your changes or refactoring efforts don't break current functionality.

I won't lie to you, retrofitting unit tests to legacy code is difficult - but it is worth it.

Solution 6 - Unit Testing

One alternative to unit tests, also introduced in Working effectively with legacy code is characterization tests. I had interesting results with such tests. They're easier to set-up than unit tests as you test from point than can be tested (called seam). The drawback is that when a test fails, you have less hint about the location of the problem as the area under test can be much larger than with unit tests. Logging helps here.


A unit test framework such those of the xUnit family can be used to write characterization tests.

In such tests, written after the facts, assertions verify the current behavior of the code. Unlike unit tests, they do not prove that the code is correct, they are just pinning down (characterizing) the current behavior of the code.

The process is similar as the TDD one,:

  • write a test for a portion of code
  • execute it - fail
  • fix the test from the observed behavior of the code
  • execute it - pass
  • repeat

The tests will fail if you modify the external behavior of the code. External behavior of the code ? sounds familiar ? Yes it is, here we are. Now you can refactor the code.

Obviously the risk depends on the coverage of the characterization tests.

Solution 7 - Unit Testing

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.

Solution 8 - Unit Testing

I've been speaking some time ago about idea of Reversed Tests Pyramid in Legacy Code at XPDays http://xpdays.com.ua/archive/xp-days-ukraine-2012/materials/legacy-code/

This presentation should answer the question why it is so important sometimes to start with integration/functional or even high level acceptance tests when working with legacy code. And then slowly, step by step introducing unit tests. There are no code examples - sorry, but you can find bunch of them in Michaels Feathers book "Working effectively with Legacy Code".

Also you can check Legacy Code Retreat http://www.jbrains.ca/legacy-code-retreat and look for that meeting in your area.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBuckeyeSoftwareGuyView Question on Stackoverflow
Solution 1 - Unit TestingJames BlackView Answer on Stackoverflow
Solution 2 - Unit TestingPhillip NganView Answer on Stackoverflow
Solution 3 - Unit TestingTrueWillView Answer on Stackoverflow
Solution 4 - Unit TestingzavgView Answer on Stackoverflow
Solution 5 - Unit TestingAndrew HareView Answer on Stackoverflow
Solution 6 - Unit TestingphilantView Answer on Stackoverflow
Solution 7 - Unit TestingLynn LangitView Answer on Stackoverflow
Solution 8 - Unit TestingstreserView Answer on Stackoverflow