Singleton and unit testing

The Effective Java has the following statement on unit testing singletons

Making a class a singleton can make it difficult to test its clients, as it’s impossible to substitute a mock implementation for a singleton unless it implements an interface that serves as its type.

Can anyone explain the why this is so ?


Solution 1:

You could use reflection to reset your singleton object to prevent tests from affecting each other.

@Before
public void resetSingleton() throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
   Field instance = MySingleton.class.getDeclaredField("instance");
   instance.setAccessible(true);
   instance.set(null, null);
}

Ref: unit-testing-singletons

Solution 2:

The problem isn't testing singletons themselves; the book is saying that if a class you are trying to test depends on a singleton, then you will likely have problems.

Unless, that is, you (1) make the singleton implement an interface, and (2) inject the singleton to your class using that interface.

For example, singletons are typically instantiated directly like this:

public class MyClass
{
    private MySingleton __s = MySingleton.getInstance() ;

    ...
}

MyClass may now be very difficult to automatedly test. For example, as @Boris Pavlović notes in his answer, if the singleton's behaviour is based on the system time, your tests are now also dependent on the system time, and you may not be able to test cases that, say, depend on the day of the week.

However, if your singleton "implements an interface that serves as its type" then you can still use a singleton implementation of that interface, so long as you pass it in:

public class SomeSingleton
    implements SomeInterface
{
    ...
}

public class MyClass
{
    private SomeInterface __s ;

    public MyClass( SomeInterface s )
    {
        __s = s ;
    }

    ...
}

...

MyClass m = new MyClass( SomeSingleton.getInstance() ) ;

From the perspective of testing MyClass you now don't care if SomeSingleton is singleton or not: you can also pass in any other implementation you want, including the singleton implementation, but most likely you'll use a mock of some sort which you control from your tests.

BTW, this is NOT the way to do it:

public class MyClass
{
    private SomeInterface __s = SomeSingleton.getInstance() ;

    public MyClass()
    {
    }

    ...
}

That still works out the same at run-time, but for testing you are now again dependent on SomeSingleton.

Solution 3:

Mocks require interfaces, because what you're doing is replacing the real underlying behavior with an imposter that mimics what you need for the test. Since the client only deals with an interface reference type, it doesn't need to know what the implementation is.

You can't mock a concrete class without an interface, because you can't replace the behavior without the test client knowing about it. It's a completely new class in that case.

It's true for all classes, Singleton or not.

Solution 4:

I think it actually depends on the implementation of the singleton access pattern.

For example

MySingleton.getInstance()

Might be very dificult to test while

MySingletonFactory mySingletonFactory = ...
mySingletonFactory.getInstance() //this returns a MySingleton instance or even a subclass

Doesn't provide any information about the fact that its using a singleton. So you can freely replace your factory.

NOTE: a singleton is defined by being only one instance of that class in an application, however the way it's obtained or stored doesn't have to be through static means.

Solution 5:

It's oh so simple.

In unit-testing, you want to isolate your SUT (the class you're testing). You don't want to test a bunch of classes, because that would defeat the purpose of unit-testing.

But not all classes do everything on their own, right? Most classes use other classes to do their work, and they kind of mediate between other classes, and add a bit of their own, to get the final result.

The point is - you don't care about how the classes your SUT depends on work. You care how your SUT works with those classes. That's why you stub or mock the classes your SUT needs. And you can use those mocks because you can pass them in as constructor parameters for your SUT.

With singletons - the bad thing is that the getInstance() method is globally accessible. That means that you usually call it from within a class, instead of depending on an interface you can later mock. That's why it's impossible to replace it when you want to test your SUT.

The solution is not to use the sneaky public static MySingleton getInstance() method, but to depend on an interface your class needs to work with. Do that, and you can pass in test doubles whenever you need to.