« Sending People Home | Main | Team Equilibrium »

October 30, 2006

Singleton is Incompatible with Unit Testing

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.

The Singleton pattern allows only one instance of each singleton class in a system. In Java, that’s one per classloader or JVM, and in C++ it's one per executable.  That's fine when you have just one system, but each test is a little "system" too; and if each of the tests needs to use a particular singleton, they can't be complete or independent because the singleton, through it's creation characteristics, has defined the scope of the "system": it's the JVM, the .NET runtime, or the interpreter.  When you think about it, isn’t it a little arrogant for a class to decide that?

One of the easiest ways to get past the impasse is to break the singleton property, the requirement that there can be only one created instance.  If you can provide some way to set a singleton’s instance, you’re on the path to create a fakable singleton, or fingleton.  You can use a configuration file to dynamically load a fake for testing, or write a static setter to set the instance. In some languages, you can use reflective tricks, or even conditionally compile the setter method so that it is only available when you are testing, but in the end, you are able to create more than one instance of that class: what you have is a fingleton, not a singleton.

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.  There are many protection mechanisms, but it seems that as they become more elaborate, the object becomes harder to use in a testing situation. Sometimes the simplest answer is the best answer: supply a static method on your singleton called setTestingInstance.. unprotect your constructor and introduce an interface.  Create mocks for your "singleton".. eventually you may be able to pass it around by reference rather than using it globally. 

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.

TrackBack

TrackBack URL for this entry:
http://www.typepad.com/services/trackback/6a00d8341d798c53ef00d83461ee3869e2

Listed below are links to weblogs that reference Singleton is Incompatible with Unit Testing:

» Unit Testing Ruby Singletons from watch this nbsp;
I found an interesting post via reddit about how the Singleton pattern is incompatible with Unit Testing. Ive certainly encountered difficulties unit testing a Singleton that maintains any sort of state. I posted the following as a comment, but... [Read More]

Comments

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.

"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?

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".

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...

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.

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.

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

Verify your Comment

Previewing your Comment

This is only a preview. Your comment has not yet been posted.

Working...
Your comment could not be posted. Error type:
Your comment has been posted. Post another comment

The letters and numbers you entered did not match the image. Please try again.

As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

Having trouble reading this image? View an alternate.

Working...

Post a comment