|« When are we gonna learn?||Alchemy: Benchmarks and Optimizations »|
I have written about the Singleton[^] before. As a quick review from what I previously stated, I don't think the Singleton is misunderstood, I think it is the only software design pattern that most people do understand. Those that call the Singleton an anti-pattern believe that it is overused. It's simple enough in concept compared to the other patterns, that itself may explain why it is used so often. Another criticism that I hear is that it is difficult to unit-test, or at least unit-test properly with a fresh fixture for each test. No, it's not, and I will demonstrate how.
Note: There are two solutions in this entry. The first is a convoluted exploration of searching for a solution that works. The second is a simple solution that is created by approaching the problem from a new direction.
What's the problem
To create robust unit-tests, a best-practice is to entirely tear-down any resources related to your System Under Test (SUT) object before you run another test. Each new test is run in what is referred to as a fresh-fixture. A fresh-fixture is a clean test environment where you control every aspect of the environment for your test. Therefore, you can reduce the possibilities of side-effects interfering with your tests. You are basically independently testing every aspect of your SUT.
The difficulty that we encounter when attempting to test a Singleton, is that quite often they are created as global variables at startup. That is, even before your
main entry-point function is called. With the improved version of the Singleton in which you declared a destructor, the problem becomes even more difficult.
Testability starts with your implementation
One of the most important factors that affects your chances of successfully creating a robust test harness around your software, is your software's implementation. This includes the public interface, the details that you abstract from the user and the data that you encapsulate within your implementation. One of the strengths of object-oriented programming that I think is often overlooked, is your ability to encapsulate the data, is what gives you the power to enforce the invariants of your classes.
One of the invariants of the classic Singleton, is that at most only one instance will exist for the system. How we choose to enforce that invariant will have a great affect on our ability to properly test the Singleton. Here is a classic implementation of the Singleton:
Since there is a
static member variable the data field must be declared in a .cpp file, or in some other way to guarantee only one instance exists when the linker combines the object code. Here is the rest of the implementation:
Demonstration of Usage
Before I demonstrate the testing portion, let's demonstrate the usage of this object so that we can better understand the problems we will be faced with when we attempt to test the object.
There are problems with this implementation
This implementation has problems, and we haven't even reached the part where we see if this can be tested. No explicit destructor has been declared, therefore the user is free to call
delete on the Singleton. An explicitly declared destructor is a necessity to create a safe Singleton implementation.
In both of the previous scenarios, the compiler is fine with the way the user interacted with the object. However, you will have a dangling pointer and no way to recover from it. You have a few options.
- Declare the destructor
publicand assigned the instance pointer back to zero. With this case, testing with a fresh fixture is once again possible and this article become a moot point:
- Declare the destructor non-public, and the user will not be able to explicitly
deletethe Singleton instance. I would suggest
protectedaccess, because it will leave the possibility to subclass your Singleton if you choose to.
- Use the C++11 way to remove the destructor altogether:
This implementation can be tested if...
If you selected the second option above for declaring your destructor and you declared it with
protected scope, there is hope of performing best-practice unit-testing on this Singleton. However, it's not pretty. Remember, "Testability starts with your implementation."
If you have declared your destructor protected, we can derive a
SingletonTest class to give us access to the destructor. Now for the ugly part. You must define your destructor to be of this form:
That's a curious bit of code isn't it? Why not just call
Because, we are dealing with a paradox.Think about it.
What type of pointer is m_pInstance?
Singleton class pointer.
To which class does the destructor with this code belong?
... and we typically get into a classes destructor by calling...
So how did we get into the destructor for the class
Singleton if we haven't yet called
delete on the only instance of this class? Furthermore, what would happen if we just deleted the pointer, then zeroed it out?
This is the part where I give you the Ocean's 11 style breakdown. We'll start with what you saw, or at least think you saw.
As I mentioned, we will start with a sub-class derived from the original Singleton.
We create a
Teardown routine for this test suite:
Here is a typical logical flow of a few tests in our test harness were structured like this:
If we instrumented the constructor and destructor for both the Singleton and TestSingleton objects, we would see something like this:
Test 1 Construct Singleton. Construct TestSingleton Construct Singleton. Destroy TestSingleton Destroy Singleton. Test 2 Construct Singleton. Construct TestSingleton Construct Singleton. Destroy TestSingleton Destroy Singleton.
Why is the constructor for the
Singleton getting called twice? That is because it is getting called once when the
TestSingleton is created, and once when the real singleton is created by calling
Singleton::Instance(). As simple as the class is now, my guess is that would not work so well on a production class singleton, especially if the object was constrained due to system resources.
We simply cannot allow the constructor of a singleton to be called twice. Furthermore, all of that fancy guard code I told you to put into place in the destructor is there to prevent an endless loop of destruction from being called. Honestly. It's in an endless recursive loop that will continue until a stack-overflow occurs. We can't really have the destructor of a singleton to be called twice either. The way we structured the
Singleton::~Singleton() function is about as clean as we will be able to get.
So how do we avoid double construction, enter the paradox, and get away before Terry Benedict realizes we got away with all of his money? We'll use a pinch. Just kidding, we are going to basically use the
TestSingleton as a Trojan Horse to get us access to the protected destructor of the base
Singleton class. However, we will not ever construct the
TestSingleton. Also, the way I instructed you to organize the destructor of the
Singleton will make it safe, for the time being.
We can allocate memory for an object, and avoid calling its constructor by calling operator
new directly. Yes, you can do that. It essentially performs the same action as calling
malloc. To make this work, we need to make three adjustments.
Setupto allocate memory without calling the constructor:
- Teardown to free memory without calling the destructor:
- Publish a method to delete the
Watch the solution for yourself:
Test 1 Construct Singleton. pObj is 00F9E348. Destroy TestSingleton Instance is 00F9E348. Destroy Singleton. Instance is 00000000. Test 2 Construct Singleton. pObj is 00F95CA0. Destroy TestSingleton Instance is 00F95CA0. Destroy Singleton. Instance is 00000000.
// The object addresses will vary on output
Admittedly, that is a very convoluted solution, and it even made our original code more fragile for maintenance. There are much better ways. In order to find this solution, we will need to approach the problem from a different direction.
Testability starts with your implementation
One of the most important factors that affects, yes, yes, yes; I already explained this, and hopefully I just proved my point. The implementation for that
Singleton sucks (rates low on the testability scale). Here is a much simpler implementation, and solution for testing, which does not resort to using
If you are considering adding
friend to make a class testable, Don't do it.
There is always a better way when testing.
Start with the same header implementation for your
Singleton. Place the implementation for the Singleton's static members from above in their own file, separate from the main Singleton.cpp implementation. For example, a file called Singleton_instance.cpp. Now structure your code similar to below:
A better way, and simpler too
When it comes time to test, replace the file Singleton_instance.cpp with one that provides flexibility to erase and create new instances of the
Singleton. For example, Test_singleton_instance.cpp:
Only two last adjustments to
We have just introduced a way to get access to the
Singleton's non-public destructor, and we didn't even need to resort to some of the subversive techniques that are possible in C++. All that was required was to use two compilation units to implement the
Singleton, rather than one as is traditionally done.
If you're concerned that we didn't test the
Singleton::Instance() function, simply create a new test harness for this portion of the object implementation. With the original implementation only two tests are required.
- Verify the object is created correctly on the first call.
- Verify the same object is returned on each successive call.
We solved a problem that seemed difficult, if not impossible at first without compromising the integrity of the SUT. With an open mind, and a bit of ingenuity, we were able to find a very clean solution. More importantly, we did not arrive at our clean solution on the first attempt. Software development is an iterative process. If you are not satisfied with your first solution, explore other possibilities. Especially for C++.