There are a lot of legacy applications out there without unit-tests. It would be very nice to have them, especially when you’re asked to fix a bug there. So, how can you start unit testing legacy applications? Lets start with a simple mantra.
Test one unit at a time.
These are unit-tests after all. There is one important thing that follows from that.
When you write a test for a specific unit, you can assume the rest of the world has been tested and works perfectly.
Which is true, since there should be individual tests for “the rest of the world” as well!
An example case
Lets do a little case here: say your legacy application is a top-down racing game that looks like this.
The Vehicle class handles the movement of any vehicle, assuming that it’s subclass sets the correct speed. It needs the Input class to exist, and function properly.
If we want to test one unit at a time, which unit should we pick for our first unit-test? A unit can refer to a class, or a function in it. Lets pick a class in this case.
There are a couple reasons to test the Input class first:
- It has no dependencies, so it’s very easy to write a unit-test for.
- When unit-testing something your target is to find bugs in the unit you’re testing. It should never fail on an error in a related class*.
- So testing it will give you a solid foundation to build on.
Following the same reasoning, we’ll test Vehicle before Car, since Car depends on Vehicle. In the end we can write one for Car.
Unit testing legacy applications: Prefer composition over inheritance
Once you’ve validated that Input is working**, it’s time to look at Vehicle. How how will we test Vehicle, an abstract class? Testing an abstract class is very hard, so preferably it should be avoided. This is where one of the other mantras of Software Engineering comes in.
Prefer composition over inheritance
So prefer to “use” a class rather then to extend it. In this case it’s reasonably easy to fix, by extracting the functionality of Vehicle into a small utility class: MovingPosition***. It’s a class with no abstract methods, and hence easier to test.
So, this makes it really easy to write a unit-test for a MovingPosition, which only leaves us the Car to worry about. That however, is reserved for a followup of this post!
The small print
The class diagrams are made using PlantUML. The flowchart is generated using graphviz / dot.
* Unless the unit-test of the related class is failing as well, of course.
** For some general tips on how to write unit-tests, see this answer on stack exchange.
*** Unit testing legacy applications is hard when there are no unit-tests yet. Sometimes you have to refactor the source code to make it easier to test, and that can cause stuff to break. So be careful, since you have no safety net yet. Be sure that it’s an actual improvement if you adjust the original source code for a unit-test. Do not add methods that are only used in the unit-test!
Another consideration is that it’s probably not possible to follow all of these nice rules when unit testing legacy applications. Partially due to the size, partially due to the time you have to spend on it, or maybe because you’re new to it.