Click here to Skip to main content
15,884,298 members
Articles / DevOps / Testing

Unit Testing a Singleton in C++

Rate me:
Please Sign up or sign in to vote.
4.67/5 (3 votes)
1 May 2015CPOL8 min read 12.3K   1  
How to unit test a Singleton in C++

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:

C++

C++
// Source: Design Patterns (GoF)
// Page:   129
class Singleton
{
public:
  static Singleton* Instance();
protected:
  Singleton();
private:
  static Singleton* m_pInstance;
};

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:

C++

C++
Singleton* Singleton::m_pInstance = 0;
 
Singleton* Singleton::Instance()
{
  if (0 == m_pInstance)
  {
    m_pInstance = new Singleton;
  }
 
  return m_pInstance;
}

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.

C++

C++
// When a caller wants to reference the object,
// they use the static member function Instance().
Singleton* pObj = Singleton::Instance();
 
// However, these usages will generate compiler errors,
// because the constructor is not declared public:
 
Singleton second;  // Error    
Singleton* pThird  // Error
  = new Singleton();

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.

C++

C++
// Without a destructor explicitly defined,
// the user can do this:
std::shared_ptr<singleton>
  p_sharedObj(Singleton::Instance());
 
// As well as this.
delete Singleton::Instance();

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.

C++

C++
~Singleton() = delete;
  • Declare the destructor public and 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 delete the Singleton instance. I would suggest protected access, 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:

C++

C++
~Singleton()
{
  if (0 != m_pInstance)
  {
    // Add all of your other destruction
    // responsibilities at this point.
 
    // Hold a copy of the singleton pointer,
    // and zero out the static member-data
    // before calling delete on the object.
    Singleton* pTemp = m_pInstance;
    m_pInstance = 0;
 
    delete pTemp;
  }
}

That's a curious bit of code isn't it? Why not just call delete m_pInstance?

Because, we are dealing with a paradox. Think about it.

What type of pointer is m_pInstance?

A Singleton class pointer.

To which class does the destructor with this code belong?

The Singleton class.

... and we typically get into a classes destructor by calling...

delete!

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?

The Reveal

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.

C++

C++
class TestSingleton
  : Singleton
{
public:
  // We don't even need a default constructor
  // or destructor because the compiler gives us one.
};

We create a Setup and Teardown routine for this test suite:

C++

C++
TestSingleton *pTest = 0;
void Setup()
{
  pTest = new TestSingleton;
}
 
void Teardown()
{
  delete pTest;
  pTest = 0;
}

Here is a typical logical flow of a few tests in our test harness were structured like this:

C++

C++
// Test 1
    Setup();
    Singleton* pObj = Singleton::Instance();
    // Testy things occur here
    Teardown();
 
    // Test 2
    Setup();
    Singleton* pObj = Singleton::Instance();
    // Now testier things occur
    Teardown();

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.

The Trick

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.

  1. Change Setup to allocate memory without calling the constructor:

    C++

    C++
    void Setup()
    {
      pTest = (TestSingleton*)operator new(sizeof(TestSingleton));
    }
  2. Teardown to free memory without calling the destructor:

    C++

    C++
    void Teardown()
    {
      pTest->DestroySingleton();
      operator delete(pTest);
     
      pTest = 0;
    }
  3. Publish a method to delete the Singleton from the TestSingleton:

    C++

    C++
    void DestroySingleton()
    {
      // Here is where the paradox originates...
      // We are invoking the destructor directly.
      // Which in turn will invoke ~Singleton().
      this->~TestSingleton();
    }

Watch the solution for yourself:

C++

C++
int main(int argc, _TCHAR* argv[])
{  
  cout << "Test 1\n";
  {
    Setup();
    Singleton* pObj = Singleton::Instance();
 
    cout << "pObj is " << pObj << ".\n";
 
    Teardown();
  }
 
  cout << "Test 2\n";
  {
    Setup();
    Singleton* pObj = Singleton::Instance();
 
    cout << "pObj is " << pObj << ".\n";
 
    Teardown();
  }
 
  return 0;
}
}

(Run this code from your browser at http://codeofthedamned.com/index.php/unit-testing-a-singleton-in)

Output

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.

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 friend.

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:

C++

C++
enum TestPhase
{
  k_unknown = 0,
  k_setup,
  k_testing,
  k_teardown
};
 
TestPhase  g_testPhase = k_unknown;
// ... Same definition for m_pInstance
 
Singleton* Singleton::Instance()
{
  if (k_teardown == g_testPhase)
  {
    delete m_pInstance;
    m_pInstance = 0;
 
    return 0;
  }
  // ... continue with normal Instance logic.
}

Only two last adjustments to Setup and Teardown:

C++

C++
void Setup()
{
  g_testPhase = k_setup;
  // Add any other necessary setup code.
  g_testPhase = k_testing;
}
 
void Teardown()
{
  g_testPhase = k_teardown;
  Singleton* pEmpty = Singleton::Instance();
 
  assert(pEmpty == 0);
}

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.

Summary

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++.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Engineer
United States United States
I am a software architect and I have been developing software for nearly two decades. Over the years I have learned to value maintainable solutions first. This has allowed me to adapt my projects to meet the challenges that inevitably appear during development. I use the most beneficial short-term achievements to drive the software I develop towards a long-term vision.

C++ is my strongest language. However, I have also used x86 ASM, ARM ASM, C, C#, JAVA, Python, and JavaScript to solve programming problems. I have worked in a variety of industries throughout my career, which include:
• Manufacturing
• Consumer Products
• Virtualization
• Computer Infrastructure Management
• DoD Contracting

My experience spans these hardware types and operating systems:
• Desktop
o Windows (Full-stack: GUI, Application, Service, Kernel Driver)
o Linux (Application, Daemon)
• Mobile Devices
o Windows CE / Windows Phone
o Linux
• Embedded Devices
o VxWorks (RTOS)
o Greenhills Linux
o Embedded Windows XP

I am a Mentor and frequent contributor to CodeProject.com with tutorial articles that teach others about the inner workings of the Windows APIs.

I am the creator of an open source project on GitHub called Alchemy[^], which is an open-source compile-time data serialization library.

I maintain my own repository and blog at CodeOfTheDamned.com/[^], because code maintenance does not have to be a living hell.

Comments and Discussions

 
-- There are no messages in this forum --