What are the ways to unit test a Singleton pattern in C++? (with examples please)
问题:
回答1:
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;
}
回答2:
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:
- We can't create and destroy instances during the tests;
- We can't define our own subclasses to test how the interface is used;
- We can't provide our own dependency to the dependent class.
The easiest way to overcome these problems is to refactor the singleton class:
- Make the constructor and destructor public, moving responsibility for lifetime management out of the class;
- Make the interface abstract, allowing us to define our own implementations;
- 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.
回答3:
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.
回答4:
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.