Is the Singleton design pattern incompatible
with unit testing? I think it is. Singleton assures us that our program will
have only one instance of a particular class and that there will be a global
access point for it. Put those two ideas together,
and what you have is a global variable that can't be replaced. Doesn't sound so bad? Well, try this out: write a set of unit tests which exercise clients of
that variable. If one test modifies
global state, and another uses it, you could have one hell of a debugging chore
when your tests fail. I won’t elaborate
on the pain here. If you’ve ever encountered
it, you know about it.
I think that many of us have known that Singleton is broken for years, but we just haven't stated it out loud. There are always the little exceptions. For instance, singletons cause no trouble at all if we can’t affect state through them, but cases like that don’t come up often. If you have an immutable data structure, it's overkill to make it a singleton. Likewise, if you have something stateless like a factory, well yes, it’s great to have one distinguished instance, but what do you do if you need to mock it? There’s that singleton property, getting in the way again.
Faced with singleton's troubles, many people turn their singletons into fingletons and attempt to protect their instances as best they can.
Yes, there are cases where you might want more protection than this and in those cases you should add whatever protection you feel you need, but let’s face it, on most projects if your teammates can't refrain from calling a static method named setTestingInstance in production code, your project is probably in trouble no matter what you do. On the other hand, if they can, it's easier to keep your software testable, correct and maintainable.
There's no need to break singleton. For example, I created a class method for my database connection singleton which sets the base class to create. For testing, I use a connection to an in-memory SQLite database. For production, it's set by configuration options. In both cases, there's no need for more than one instance of the class.
Posted by: Name | October 30, 2006 at 09:35 PM
"In both cases, there's no need for more than one instance of the class."
That's not the issue. The issue is: is there a need to specifically limit your entire design to only ever be able to connect to one and only one database and should everything have access to it at all times?
Posted by: Poster from Reddit | October 31, 2006 at 02:02 AM
Funnily enough this very issue came up on a client site a little while ago. One of the developers wandered over and asked me, all innocent-like, if I might just happen to know an easy way to get JUnit to run different tests under different classloaders...
Seems to me that the GoF did us a disservice in the way they illustrated Singleton, as an implemenation choice: thus is born the one-and-only db connection and so forth with all the trouble they bring.
But Singleton is pretty harmless if used to implement value objects that model a domain concept with a single unique instance (or multiple indistinguishable instances, if you think that's different). I'm thinking of the single instances of True and False in the Smalltalk image, for instance. In the domain of Boolean algebra (which is being modelled) all "T"s are T and all "F"s are F. Singleton provides a good way to deal with that. Otherwise, pretty pernicious.
Maybe Singleton is taking over from Visitor as the "whipping boy of patterns".
Posted by: keithb | October 31, 2006 at 02:51 AM
I've encountered this problem recently using Ruby. Thankfully, with Ruby I can create a singleton of my Singleton class for use in my unit tests. In this test-specific singleton I can add "reset" or "clear" method to reset the state. Assuming I have a singleton called "OnlyOne":
def test_singleton
class << OnlyOne.instance
def reset
# reset state
end
end
OnlyOne.instance.modify_state
OnlyOne.instance.reset
end
* Code doesn't seem to keep its formatting and it wasn't clear whether or not HTML is supported...
Posted by: MrChucho | October 31, 2006 at 06:28 AM
Just had numerous problems with this same instance. A cache object that in and of itself was too big, with 100+ unrelated methods - in some cases the methods were grouped together in the header file via comments. If only there was some sort of C++ structure to group methods and data together in an ...what's the word..object...
Anyway it kept kicking my butt and I turned it into a fingleton for testing purposes. Unfortunately TDD is not universally accepted at my company, and as such I may have to go to great lengths to justify breaking the design at code review.
Hey Michael. Your buddies over at ObjectMentor just changed the entire fitnesse.org blog, and it became very difficult to find a copy of CPPUnit Lite. Any chance you'll put a copy in one location that's more easily googlable? I needed it this morning and hunted for a long time before I found it on the old fitness site.
Posted by: Eric | October 31, 2006 at 09:02 PM
Singletons only really cause issues when they are used for global variables, or for external resources (almost the same thing).
We would probably have left the global variable behind in the 80s or earlier if the GoF hadn't glorified it by calling it a Singleton.
Posted by: Ricky Clarkson | November 01, 2006 at 04:11 AM
The testing problems with Singletons have made me come up with a design pattern: the Half-A-Singleton. This pattern is halfway between the "using global variable" code and the "pass everything around" code. To make it simple: it makes passing the Singleton's instance optional. So you CAN pass it for tests, but you can still leave a lot of production code intact. See http://www.w-p.dds.nl/pathalfs.php?STYLE=4
Posted by: Willem Bogaerts | November 16, 2006 at 03:21 AM