10

What are the ways to unit test a Singleton pattern in C++? (with examples please)

Aquarius_Girl
  • 21,790
  • 65
  • 230
  • 411
  • 1
    This looks like two rather separate questions. Maybe you could move the dependency injection part into a new question? – Georg Fritzsche Feb 08 '12 at 10:32
  • @GeorgFritzsche Dependency injection is said to be a way of unit testing, isn't it? – Aquarius_Girl Feb 08 '12 at 10:34
  • "Injecting a singleton into ..." and "unit testing a singleton" are two different subjects. – Georg Fritzsche Feb 08 '12 at 10:35
  • @GeorgFritzsche The link says that "Dependency injection is said to be a way of unit testing,", is it wrong? – Aquarius_Girl Feb 08 '12 at 10:40
  • possible duplicate of [What is so bad about Singletons?](http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons) – Mike Seymour Feb 08 '12 at 11:21
  • @AnishaKaul: That's not what the question originally asked, and I can't remove my vote now you've changed it. Anyway, that question does provide useful information about how difficult it is to unit test something that's inherently untestable. – Mike Seymour Feb 08 '12 at 11:30
  • Why have you editing this question so much it bears no relation to the original question - and you have started another question in a similar vain? – Ed Heal Feb 08 '12 at 11:40
  • I really like this https://helpercode.com/2015/09/16/how-to-fake-a-singleton-in-c/ – Cpp crusaders Apr 03 '21 at 15:00

4 Answers4

14

Make the implementation of the singleton a separate class, and make a wrapper that implements the "singletonness" outside. That way you can test the implementation as much as you like (except the singleton behavior which is trivial and unnecessary.

class SingletonImpl {
public:
  int doit(double,double);
};

class Singleton {
public:
  Singleton& instance() {...}
  int doit(double a,double b) {impl->doit(a,b);}
  ...
private:
  SingletonImpl impl;
}
daramarak
  • 6,115
  • 1
  • 31
  • 50
3

Suppose we have the classic singleton anti-pattern, which is responsible for three things:

class Singleton {
public:
    // Globally accessible instance
    static Singleton & instance();

    // Public interface
    void do_something();

private:
    // Lifetime management
    Singleton();
    ~Singleton();
}

and a class that depends on this:

class Dependent {
public:
    Dependent() : s(Singleton::instance()) {}

    void do_something_else();

private:
    Singleton & s;
};

Now we would like to write a unit test for the singleton:

void test_singleton() {
    Singleton s;        // Problem 1
    s.do_something();
    assert(/* some post-condition */);
}

and for the dependent class:

struct StubSingleton : Singleton // Problem 2
{
    int did_something;

    StubSingleton : did_something(0) {}
    void do_something() {++did_something;}    
};

void test_dependent() {
    StubSingleton s;    // Problem 1
    Dependent d(s);     // Problem 3
    d.do_something_else();
    assert(s.did_something == 1);
}

We see there are three problems to overcome:

  1. We can't create and destroy instances during the tests;
  2. We can't define our own subclasses to test how the interface is used;
  3. We can't provide our own dependency to the dependent class.

The easiest way to overcome these problems is to refactor the singleton class:

  1. Make the constructor and destructor public, moving responsibility for lifetime management out of the class;
  2. Make the interface abstract, allowing us to define our own implementations;
  3. Remove the global instance, and modify dependent classes to take its dependency by reference.

So now our classes look like:

class Singleton {
public:
    virtual ~Singleton() {}
    virtual void do_something() = 0;
};

class RealSingleton : public Singleton
{
    void do_something();
};

class Dependent {
public:
    explicit Dependent(Singleton & s) : s(s) {}
    void do_something_else();
private:
    Singleton & s;
};

Now the class is easy to test, and almost as easy to use in production (you just need to create an instance of RealSingleton and pass references to it where they're needed). The only problem is that you can't call it a singleton any more.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • I'll follow up soon, here and on your other answer. Thanks. – Aquarius_Girl Feb 08 '12 at 12:17
  • In the code example the line: StubSingleton : did_something(0) {} Should actually be : StubSingleton() : did_something(0) {} // Problem 1 Because the singleton constructor is inaccessible – Denise P Oct 12 '22 at 08:59
2

This is how I would do it

class Singleton
{
    protected:
       static Singleton *instance = 0:
    public:
       Singleton &GetInstance()
       {
          if (!instance) instance = new Singleton;
          return *instance;
       }
       ...
};

Then to test I would create just for testing purposes

class TestSingleton : public Singleton
{
     public:
        void DestroyInstance() {
           if (instance) delete instance;
           instance = 0;
};

Then use TestSingleton - so you are able to perform all the test cases and ensure that are the start the instance is recreated.

Ed Heal
  • 59,252
  • 17
  • 87
  • 127
1

A singleton is just a class of which you can only have one instance. How you find that instance is not part of that pattern, so using DI with a singleton is completely fine.

Unit-testing the singleton-pattern is hard, that is one of the main argument against using it. One way to do it in C++ is to have the definition of the singleton in a separate compilation unit, so that you can link against a mock-implementation when testing classes that use the singleton.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283