Re: Unit Testing Frameworks (was Re: Singletons)
[Please do not mail me a copy of your followup]
Ian Collins <ian-news@this.is.invalid> spake the secret code
<al3diaF2gb5U1@mid.individual.net> thusly:
[...] For unit testing, you don't want to bring in all the real
dependencies of the code under test, you want to be able to monitor
and control the interfaces your unit is using. TO do that, you mock
them.
Yes, I agree completely. The difficulty of Singletons isn't that they
are a singleton per se, but that the SUT has lots of code like this:
class Single
{
public:
static Single *instance();
// other public methods but Single is not a pure virtual base
void doSomething();
void doOtherThing();
private:
Single();
~Single();
};
class SUT
{
public:
void someMethod();
// ...
};
void SUT::someMethod()
{
Single::instance()->doSomething();
// ...
Single::instance()->doOtherThing();
// ...
}
....which means I'm left only with link-time substitution as a way to
mock out the singleton if I leave this code unchanged. Consider this
minor refactoring, however:
class Single
{
public:
virtual ~Single() { }
// pure virtual base defining interface to Single
virtual void doSomething() = 0;
virtual void doOtherThing() = 0;
};
class ProductionSingle : public Single
{
public:
static Single *instance();
private:
ProductionSingle();
virtual ~ProductionSingle();
};
class SUT
{
public:
void someMethod() { someMethod(ProductionSingle::instance()); }
void someMethod(Single *single);
};
void SUT::someMethod(Single *single)
{
single->doSomething();
// ...
single->doOtherThing();
// ...
}
Now I can test SUT::someMethod without it being directly coupled to
the production singleton; in fact, there's NOTHING in SUT that
requires it's collaborator to be a singleton, but the choice of making
that collaborator a singleton "leaked" into the implementation of SUT
making it harder to test. Extracting an interface over Single and
using DI (at the method level) makes testing SUT::someMethod *much*
easier and I don't have to resort to link-time substitution in order
to test the method. Existing code that calls SUT::someMethod()
doesn't have to change.
--
"The Direct3D Graphics Pipeline" free book <http://tinyurl.com/d3d-pipeline>
The Computer Graphics Museum <http://computergraphicsmuseum.org>
The Terminals Wiki <http://terminals.classiccmp.org>
Legalize Adulthood! (my blog) <http://legalizeadulthood.wordpress.com>
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]