So you’ve had enough. That aging codebase of yours is just too darn fragile to work on. Everytime you fix a bug or add a new feature, something unexpected breaks. Your manual test plans aren’t thorough enough to catch these regressions, so the only way you find out about them is through angry customers. By the time your support team hears about them and they are triaged, groomed, prioritized, entered into a sprint, and finally fixed, it could be weeks. Not a great recipe for a successful product.

You already know that automated unit tests guard against regressions by constantly testing your code at the lowest level, but your legacy codebase is a mess of singletons and static method calls that are impossible to mock. Or are they? Here are some techniques you can use to make that code testable without making drastic (and risky) changes. The examples shown are written in C# but the concepts should apply to any object-oriented language.

Mocking Singletons

Here is an example of C# code that calls out to a singleton. How can we write a unit test that verifies that calling Bar() actually invokes DoIt() ?

public class Foo

{

public void Bar()

{

Helper.Instance().DoIt();

}

}

We assume that Helper implements the IHelper interface. If it does not, and the class is not modifiable, use the technique shown in the Mocking Static Methods section below.

Let’s create a private member variable to hold onto an instance of IHelper , and a constructor that sets that variable. This is known as the constructor form of dependency injection. We will use this constructor in our unit tests to inject a mock version of IHelper and verify that DoIt() was called. However, the trick to not breaking the current implementation is to also provide a default constructor that gets its value from the existing singleton.

public class Foo

{

private readonly IHelper _helper; public Foo()

{

_helper = Helper.Instance();

} public Foo(IHelper helper)

{

_helper = helper;

} public void Bar()

{

_helper.DoIt();

}

}

We can use constructor chaining to avoid duplicating code:

public Foo() : this(Helper.Instance())

{

}

Now your code is testable, but perfectly backwards-compatible! Here is what a unit test would look like using Xunit as the test runner and Moq as the mocking framework: