/* Code Musings */

Unit Testing: Singletons

Singletons. There is a time and place that a developer can use the Singleton Pattern, but in my experience these situations are rare. When I first learned about this design pattern, I, like many novice developers, decided to use it everywhere. The very first incarnation of my game engine used it in a variety of places, such as for logging or resource management. But it wasn’t until my tenure at BumpTop that I realized the problem with Singletons.

Singletons make unit testing difficult.
A singleton has a lifetime that spans the entirety of the application, due to the fact that you can only create it once. There is no way to reset or alter the singleton without adding an extraneous amount of getter and setting functions. One of my favourite ways of unit testing is inheriting from the class that is being tested and utilizing the using keyword to change the level of access control of member variables. Take for example the following class:

To write a comprehensive unit test for this contrived example, you should be able to test the function by testing the result it has on its subject. The subject, in this case, is the member variable ‘bar’. Since the member variable is not accessible to the outside world, you could inherit from it and test it that way:

Now you can easily test the subject after calling the function that modifies it.

So, why can’t you do this with a singleton?
Well, you can but many people insist that if you’re designing a singleton, you should not allow it to be instantiated without calling a single static construction function. Thus, many developers mark the constructor and copy constructor as private. Having this limitation forbids inheritance using a test harness class, like the one above, and limits the robustness of unit tests.

But you can use multiple inheritance to fix this!
A solution that I sometimes propose, albeit reluctantly, is that the business logic of the singleton is decoupled from the Singleton Pattern itself. A templated Singleton class, which are plentiful, would handle the creation and lifetime management of the business logic class while leaving the business logic class in a state that is easily testable. It doesn’t solve the problem limited extendability and masking coupled classes, but it at least gives you the option of writing robust unit tests.

Leave a Reply