Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?

like image 929
user2434 Avatar asked Nov 24 '11 12:11

user2434


People also ask

Why singleton is hard to unit test?

It's very difficult to write unit tests for code that uses singletons because it is generally tightly coupled with the singleton instance, which makes it hard to control the creation of singleton or mock it.

How do you unit test a singleton class?

First of all, extract an interface from the Singleton class and then apply Dependency Injection. We need interface for decoupling purpose and unit testing. Accessing singleton class from another class, the instance would then be passed to any objects that might trying to implement it through the property.

Can we write unit test for singleton class?

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.

How do singleton affect testing?

If your singleton contains mutable state that cannot be reset between tests, then it will affect unit testing as the tests will need to be run in a specific order and the state accounted for. If your singleton contains state that changes of time, it will prevent you reliably running tests in parallel.


2 Answers

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

like image 164
TedEd Avatar answered Sep 24 '22 02:09

TedEd


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.

like image 30
Rodney Gitzel Avatar answered Sep 23 '22 02:09

Rodney Gitzel