Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking a singleton with mockito

I need to test some legacy code, which uses a singleton in a a method call. The purpose of the test is to ensure that the clas sunder test makes a call to singletons method. I have seen similar questions on SO, but all the answers require other dependencies (different test frameworks) - I'm unfortunately limited to using Mockito and JUnit, but this should be perfectly possible with such popular framework.

The singleton:

public class FormatterService {      private static FormatterService INSTANCE;      private FormatterService() {     }      public static FormatterService getInstance() {         if (INSTANCE == null) {             INSTANCE = new FormatterService();         }         return INSTANCE;     }      public String formatTachoIcon() {         return "URL";     }  } 

The class under test:

public class DriverSnapshotHandler {      public String getImageURL() {         return FormatterService.getInstance().formatTachoIcon();     }  } 

The unit test:

public class TestDriverSnapshotHandler {      private FormatterService formatter;      @Before     public void setUp() {          formatter = mock(FormatterService.class);          when(FormatterService.getInstance()).thenReturn(formatter);          when(formatter.formatTachoIcon()).thenReturn("MockedURL");      }      @Test     public void testFormatterServiceIsCalled() {          DriverSnapshotHandler handler = new DriverSnapshotHandler();         handler.getImageURL();          verify(formatter, atLeastOnce()).formatTachoIcon();      }  } 

The idea was to configure the expected behaviour of the dreaded singleton, since the class under test will call it's getInstance and then formatTachoIcon methods. Unfortunately this fails with an error message:

when() requires an argument which has to be 'a method call on a mock'. 
like image 477
fbielejec Avatar asked Aug 12 '16 09:08

fbielejec


People also ask

Can Mockito mock Singleton?

If you are using Mockito 3.4. 0+, you may mock a singleton like the following, without PowerMock or other dependencies.

How do you make a mock for Singleton?

There is a way to mock Singleton. Use powermock to mock static method and use Whitebox to invoke constructor YourClass mockHelper = Whitebox . invokeConstructor(YourClass. class); Whitebox.

What is Singleton socket?

Allow a single instance of a Python application to run at once.

How do you add a mock maker inline?

Option 2: Adding a file to enable the Mock maker inlineCreate a resources directory in your test directory if you do not have one. In resources create the directory mockito-extensions and in that directory the file org. mockito. plugins.


2 Answers

What you are asking is not possible because your legacy code relies on a static method getInstance() and Mockito does not allow to mock static methods, so the following line won't work

when(FormatterService.getInstance()).thenReturn(formatter); 

There are 2 ways around this problem:

  1. Use a different mocking tool, such as PowerMock, that allows to mock static methods.

  2. Refactor your code, so that you don't rely on the static method. The least invasive way I can think of to achieve this is by adding a constructor to DriverSnapshotHandler that injects a FormatterService dependency. This constructor will be only used in tests and you production code will continue to use the real singleton instance.

    public static class DriverSnapshotHandler {      private final FormatterService formatter;      //used in production code     public DriverSnapshotHandler() {         this(FormatterService.getInstance());     }      //used for tests     DriverSnapshotHandler(FormatterService formatter) {         this.formatter = formatter;     }      public String getImageURL() {         return formatter.formatTachoIcon();     } } 

Then, your test should look like this :

FormatterService formatter = mock(FormatterService.class); when(formatter.formatTachoIcon()).thenReturn("MockedURL"); DriverSnapshotHandler handler = new DriverSnapshotHandler(formatter); handler.getImageURL(); verify(formatter, atLeastOnce()).formatTachoIcon(); 
like image 85
noscreenname Avatar answered Sep 19 '22 01:09

noscreenname


I think it is possible. See an example how to test a singleton

Before a test:

@Before public void setUp() {     formatter = mock(FormatterService.class);     setMock(formatter);     when(formatter.formatTachoIcon()).thenReturn(MOCKED_URL); }  private void setMock(FormatterService mock) {     try {         Field instance = FormatterService.class.getDeclaredField("instance");         instance.setAccessible(true);         instance.set(instance, mock);     } catch (Exception e) {         throw new RuntimeException(e);     } } 

After the test - it is important to clean up the class, because other tests will be confused with the mocked instance.

@After public void resetSingleton() throws Exception {    Field instance = FormatterService.class.getDeclaredField("instance");    instance.setAccessible(true);    instance.set(null, null); } 

The test:

@Test public void testFormatterServiceIsCalled() {     DriverSnapshotHandler handler = new DriverSnapshotHandler();     String url = handler.getImageURL();      verify(formatter, atLeastOnce()).formatTachoIcon();     assertEquals(MOCKED_URL, url); } 
like image 26
Kyrylo Semenko Avatar answered Sep 17 '22 01:09

Kyrylo Semenko