Void’s Vault

Knowledge source for efficiency.

Unit-testable Version of the Singleton

One of my friends, Jean-Nicolas Viens (JNI) found this tweaked version of the singleton pattern. It make possible to inject mocks, so unit tests are still possible!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class RepositoryLocator {
    private static RepositoryLocator instance;

    public static RepositoryLocator getInstance() {
        if (instance == null) {
            throw new RepositoryLocatorNotInitializedException();
        }
        return instance;
    }

    public static void load(RepositoryLocator locator) {
        instance = locator;
    }

    public RepositoryLocator() {
    }

Ok, I admit, it does really look weird one the first overview. There’s a public constructor, a load method to set the instance and a getInstance. But take a deeper look. You only have to setup the instance one like this :

1
RepositoryLocator.load(new RepositoryLocator());

Then the usage everywhere in the code is as with any singleton :

1
RepositoryLocator.getInstance();

But this only little load method make possible to inject mocks!

1
2
private RepositoryLocator mockedLocator = mock(RepositoryLocator.class);
RepositoryLocator.load(mockedLocator);

And there you go! You can stub any method in the singleton. When a class will call the getInstance method, the mock will be returned so you ensure unit tests.

Also, here’s a variant for the ones that code in .NET. If you don’t want to pollute your production code with test code, you could do this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Public Class CacheHelper
    Protected Shared instance As CacheHelper

    Public Shared Function GetInstance() As CacheHelper
        If (instance Is Nothing) Then
            instance = New CacheHelper()
        End If
        Return instance
    End Function

    Protected Sub New()
    End Sub

    'Rest of the class...
End Class

Then, in a test project, create this fixture.

1
2
3
4
5
6
7
8
9
10
Public Class CacheHelperFixture
    Inherits CacheHelper

    Public Sub New()
    End Sub

    Friend Shared Sub Load(cacheHelper As CacheHelper)
        instance = cacheHelper
    End Sub
End Class

Through it, you will be able to inject a mock in the singleton so that the classes are isolated from it.

Enjoy!