Singleton and unit testing

JavaUnit TestingSingleton

Java Problem Overview


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 ?

Java Solutions


Solution 1 - Java

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

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

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

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

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.

Solution 6 - Java

Singleton objects are created without any control from the outside. In one of the other chapters of the same book Bloch suggests using enums as default Singleton implementation. Let's see an example

public enum Day {
  MON(2), TUE(3), WED(4), THU(5), FRI(6), SAT(7), SUN(1);

  private final int index;

  private Day(int index) {

    this.index = index;
  }

  public boolean isToday() {
  
    return index == new GregorianCalendar().get(Calendar.DAY_OF_WEEK);
  }
}

Let's say we have a code that should be executed only on weekends:

public void leisure() {

  if (Day.SAT.isToday() || Day.SUN.isToday()) {

    haveSomeFun();
    return;
  }

  doSomeWork();
}

Testing leisure method is going to be pretty hard. Its execution is going to be dependent on the day when it is executed. If it executes on a weekday doSomeWork() will be invoked and on weekends haveSomeFun().

For this case we would need to use some heavy tools like PowerMock to intercept the GregorianCalendar constructor, return a mock which will return an index corresponding to a weekday or weekend in two test cases testing both execution paths of the leisure method.

Solution 7 - Java

it’s impossible to substitute a mock implementation for a singleton

This is not true. You can subclass your singleton and setter inject a mock. Alternatively, you can use PowerMock to mock static methods. However the need to mock singletons can be symptomatic of poor design.

The real problem is Singletons when abused turn into dependency magnets. Since they are accessible everywhere, it can appear more convenient to put the functions you need in them rather than delegating to an appropriate class, especially for programmers new to OOP.

The testability problem is now you have a bunch of Singletons that are accessed by your object under test. Even though the object probably only uses a small fraction of methods in the Singletons, you still need to mock each Singleton and figure out which methods are depended on. Singletons with a static state (Monostate pattern) are even worse because you can have to figure out which interactions between objects are affected by the Singleton's state.

Used carefully, Singletons and testability can occur together. For instance, in absence of a DI framework, you can use Singletons as your Factories and ServiceLocators, which you can setter inject to create a fake service layer for your end-to-end tests.

Solution 8 - Java

It is possible, see the example

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.lang.reflect.Field;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class DriverSnapshotHandlerTest {

private static final String MOCKED_URL = "MockedURL";
private FormatterService formatter;

@SuppressWarnings("javadoc")
@Before
public void setUp() {
    formatter = mock(FormatterService.class);
    setMock(formatter);
    when(formatter.formatTachoIcon()).thenReturn(MOCKED_URL);
}

/**
 * Remove the mocked instance from the class. It is important, because other tests will be confused with the mocked instance.
 * @throws Exception if the instance could not be accessible
 */
@After
public void resetSingleton() throws Exception {
   Field instance = FormatterService.class.getDeclaredField("instance");
   instance.setAccessible(true);
   instance.set(null, null);
}

/**
 * Set a mock to the {@link FormatterService} instance
 * Throws {@link RuntimeException} in case if reflection failed, see a {@link Field#set(Object, Object)} method description.
 * @param mock the mock to be inserted to a class
 */
private void setMock(FormatterService mock) {
    Field instance;
    try {
        instance = FormatterService.class.getDeclaredField("instance");
        instance.setAccessible(true);
        instance.set(instance, mock);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

/**
 * Test method for {@link com.example.DriverSnapshotHandler#getImageURL()}.
 */
@Test
public void testFormatterServiceIsCalled() {
    DriverSnapshotHandler handler = new DriverSnapshotHandler();
    String url = handler.getImageURL();

    verify(formatter, atLeastOnce()).formatTachoIcon();
    assertEquals(MOCKED_URL, url);
}

}

Solution 9 - Java

Use PowerMock to mock Singleton class (SingletonClassHelper) instance and non-static method (nonStaticMethod) which is called in task.execute().

    import static org.mockito.Mockito.when;

    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.Mockito;
    import org.powermock.api.mockito.PowerMockito;
    import org.powermock.core.classloader.annotations.PrepareForTest;
    import org.powermock.modules.junit4.PowerMockRunner;

    @PrepareForTest({ SingletonClassHelper.class })
    @RunWith(PowerMockRunner.class)
    public class ClassToTest {
    	
    	@InjectMocks
        Task task;
    	
    	private static final String TEST_PAYLOAD = "data";
    	private SingletonClassHelper singletonClassHelper;
    	
    	
    	@Before
    	public void setUp() {
    		PowerMockito.mockStatic(SingletonClassHelper.class);
    		singletonClassHelper = Mockito.mock(SingletonClassHelper.class);
    		when(SingletonClassHelper.getInstance()).thenReturn(singletonClassHelper);
    	}
    
    	@Test
    	public void test() {
    		when(singletonClassHelper.nonStaticMethod(parameterA, parameterB, ...)).thenReturn(TEST_PAYLOAD);
            task.execute();
    	}
    }

Solution 10 - Java

durian-globals does lazy double-locked initialization of singletons, but also has a simple test-only API which allows you to replace the implementation for unit testing.

Solution 11 - Java

As far as I know, a class implementing a Singleton cannot be extended (superclass constructor is always called implicitly and the constructor in a Singleton is private). If you want to mock a class you have to extend the class. As you see in this case it wouldn't be possible.

Solution 12 - Java

The problem with singletons (and also with static methods) is that it makes it hard to replace the actual code with a mocked implementation.

For example consider the following code

public class TestMe() {
  public String foo(String data) {
    boolean isFeatureFlag = MySingletonConfig.getInstance().getFeatureFlag();
    if (isFeatureFlag)
      // do somethine with data
    else
      // do something else with the data
     return result;
  }
}

It is not easy to write a unit test for the foo method and verifying the correct behavior is performed. This is because you can't easily change the return value of getFeatureFlag.

The same problem exists for static methods - it's not easy to replace the actual target class method with a mock behavior.

Sure, there are workarounds like powermock, or dependency injection to the method, or reflection in tests. But it is much better not to use singletons in the first place

Solution 13 - Java

Below there is the solution I had to adopt with some immutable Kotlin singleton to test them

Suppose you have a singleton class like this:

class MySingleton private constructor(
  {your dependencies}
) {
  companion object {
    @JvmStatic
    private var INSTANCE: MySingleton? = null

    @JvmStatic
    fun getInstance(): MySingleton {
      return INSTANCE ?: synchronized(this) {
        INSTANCE ?: MySingleton(
          {your dependencies}
        ).also {
          INSTANCE = it
        }
      }
    }
  }
}

You can do this in your kotlin junit tests:

@After
fun after() {
  val instance = MySingleton.Companion::class.memberProperties.find {
    it.name == "INSTANCE"
  }
  instance!!.isAccessible = true
  instance.javaField!!.set(null, null)
}

You just need to add the kotlin-reflect artifact to your dependencies

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser2434View Question on Stackoverflow
Solution 1 - JavaTedEdView Answer on Stackoverflow
Solution 2 - JavaRodney GitzelView Answer on Stackoverflow
Solution 3 - JavaduffymoView Answer on Stackoverflow
Solution 4 - JavaLiviu T.View Answer on Stackoverflow
Solution 5 - JavaYam MarcovicView Answer on Stackoverflow
Solution 6 - JavaBoris PavlovićView Answer on Stackoverflow
Solution 7 - JavaGarrett HallView Answer on Stackoverflow
Solution 8 - JavaKyrylo SemenkoView Answer on Stackoverflow
Solution 9 - JavaYao LiView Answer on Stackoverflow
Solution 10 - JavaNed TwiggView Answer on Stackoverflow
Solution 11 - JavaDPMView Answer on Stackoverflow
Solution 12 - JavaTzafrirView Answer on Stackoverflow
Solution 13 - JavaMatPagView Answer on Stackoverflow