mocking a singleton class

2022-08-31 16:56:13

I recently read that making a class singleton makes it impossible to mock the objects of the class, which makes it difficult to test its clients. I could not immediately understand the underlying reason. Can someone please explain what makes it impossible to mock a singleton class? Also, are there any more problems associated with making a class singleton?


答案 1

Of course, I could write something like don't use singleton, they are evil, use Guice/Spring/whatever but first, this wouldn't answer your question and second, you sometimes have to deal with singleton, when using legacy code for example.

So, let's not discuss the good or bad about singleton (there is another question for this) but let's see how to handle them during testing. First, let's look at a common implementation of the singleton:

public class Singleton {
    private Singleton() { }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    public String getFoo() {
        return "bar";
    }
}

There are two testing problems here:

  1. The constructor is private so we can't extend it (and we can't control the creation of instances in tests but, well, that's the point of singletons).

  2. The is static so it's hard to inject a fake instead of the singleton object in the code using the singleton.getInstance

For mocking frameworks based on inheritance and polymorphism, both points are obviously big issues. If you have the control of the code, one option is to make your singleton "more testable" by adding a setter allowing to tweak the internal field as described in Learn to Stop Worrying and Love the Singleton (you don't even need a mocking framework in that case). If you don't, modern mocking frameworks based on interception and AOP concepts allow to overcome the previously mentioned problems.

For example, Mocking Static Method Calls shows how to mock a Singleton using JMockit Expectations.

Another option would be to use PowerMock, an extension to or which allows to mock stuff normally not mock-able like static, final, private or constructor methods. Also you can access the internals of a class.MockitoJMock


答案 2

The best way to mock a singleton is not to use them at all, or at least not in the traditional sense. A few practices you might want to look up are:

  • programming to interfaces
  • dependency injection
  • inversion of control

So rather than having a single you access like this:

Singleton.getInstance().doSometing();

... define your "singleton" as an interface and have something else manage it's lifecycle and inject it where you need it, for instance as a private instance variable:

@Inject private Singleton mySingleton;

Then when you are unit testing the class/components/etc which depend on the singleton you can easily inject a mock version of it.

Most dependency injection containers will let you mark up a component as 'singleton', but it's up to the container to manage that.

Using the above practices makes it much easier to unit test your code and lets you focus on your functional logic instead of wiring logic. It also means your code really starts to become truly Object Oriented, as any use of static methods (including constructors) is debatably procedural. Thus your components start to also become truly reusable.

Check out Google Guice as a starter for 10:

http://code.google.com/p/google-guice/

You could also look at Spring and/or OSGi which can do this kind of thing. There's plenty of IOC / DI stuff out there. :)