Here's a refactoring I often teach. It's particularly applicable if you're wanting to refactor legacy code. I'll show it in C++ in honour of the excellent developers at
Promethean.
I'll start with a typical Singleton:
class singleton
{
public:
static singleton & instance();
void f(int value);
int g(const std::string & path);
private:
singleton();
~singleton();
};
Together with typical piece of client client code that I'm wanting to unit-test:
int client::eg1()
{
stuff();
more_stuff();
int r = singleton::instance().g("Hello world");
return yet_more_stuff(r);
}
My problem is that singleton leads to one or more external dependencies I'd like my unit-tests to bypass. My first step is to create an interface for the singleton:
class singleton_interface
{
public:
virtual void f(int value) = 0;
virtual int g(const std::string & path) = 0;
...
};
And then make singleton implement the interface.
(The saving grace of singleton is that its methods are at least instance methods.)
class singleton : public singleton_interface
{
public:
static singleton & instance();
void f(int value);
int g(const std::string & path);
private:
singleton();
~singleton();
};
Now I can overload eg1 as follows:
int client::eg1(singleton_interface & instance)
{
stuff();
more_stuff();
int r = instance.g("Hello world");
return yet_more_stuff(r);
}
int client::eg1()
{
return eg1(singleton::instance());
}
And finally, I can create singleton_interface sub-classes and use them in my unit-tests:
struct my_mock_singleton : singleton_interface
{
explicit my_mock_singleton(int g_result)
: g_result(g_result)
{
}
void f(int value)
{
}
int g(const std::string & s)
{
return g_result;
}
int g_result;
};
void test_client_eg1_hitch_hiker_is_42()
{
assert(42 == client().eg1(my_mock_singleton(6*9)));
}