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'.
Advertisement
Answer
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:
Use a different mocking tool, such as PowerMock, that allows to mock static methods.
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 aFormatterService
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();