2016-08-12 6 views
8

Мне нужно протестировать некоторый старый код, в котором используется одноэлементный вызов метода. Целью теста является обеспечение того, чтобы тест clas sunder вызывал метод одиночных чисел. Я видел похожие вопросы по SO, но все ответы требуют других зависимостей (разные тестовые рамки). К сожалению, я ограничена использованием Mockito и JUnit, но это должно быть вполне возможно с такой популярной структурой.Mocking a singleton with mockito

Одноэлементные:

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"; 
    } 

} 

Класса испытываемый:

public class DriverSnapshotHandler { 

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

} 

Тест блока:

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(); 

    } 

} 

Идея заключалась в том, чтобы настроить ожидаемое поведение страшных одноэлементный, так как тестируемый класс будет называть его getInstance, а затем formatTachoIcon. К сожалению, это не удается с сообщением об ошибке:

when() requires an argument which has to be 'a method call on a mock'. 
+0

Вы не можете сделать это в Mockito, не пользуясь PowerMock, если вы не реорганизовать один из ваших классов. Но я не уверен, почему ты хочешь. Вы тестируете метод только с одной строкой и без внутренней логики. Это не может потерпеть неудачу. Проведите тестирование в другом месте. –

+0

«Целью теста является обеспечение того, чтобы тест clas sunder вызывал метод одиночных игр». Ни один хороший тестовый пример не должен иметь такого рода цели.Вместо этого постарайтесь проверить некоторые значимые бизнес-функции. Вызвать зависимость и проверить метод называется не обязательно неправильным, но это нужно делать только тогда, когда это необходимо. –

ответ

9

Что вы спрашиваете, не представляется возможным, потому что ваш старый код основан на статическом методе getInstance() и Mockito не позволяет издеваться статические методы, так что следующая строка не будет работать

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

Есть 2 способа вокруг этой проблемы:

  1. Используйте другой инструмент для издевательств, такой как PowerMock, который позволяет имитировать статические методы.

  2. Рефакторинг вашего кода, чтобы вы не полагались на статический метод. Наименее инвазивным способом, который я могу придумать для достижения этого, является добавление конструктора к DriverSnapshotHandler, который вводит зависимость FormatterService. Этот конструктор будет использоваться только в тестах, и ваш производственный код будет продолжать использовать реальный экземпляр singleton.

    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(); 
        } 
    } 
    

Затем ваш тест должен выглядеть следующим образом:

FormatterService formatter = mock(FormatterService.class); 
when(formatter.formatTachoIcon()).thenReturn("MockedURL"); 
DriverSnapshotHandler handler = new DriverSnapshotHandler(formatter); 
handler.getImageURL(); 
verify(formatter, atLeastOnce()).formatTachoIcon(); 
+0

Третий способ был бы признать это (возможно) лучший тест может быть написан без насмешивания синглтона. В этом случае мы не можем точно знать, так как это вопрос [XY] (http://xyproblem.info). –

0

Ваш деЫпзЬапсе Митос является статическим, таким образом, не может быть издевались с помощью Mockito. http://cube-drone.com/media/optimized/172.png. Для этого вы можете использовать PowerMockito. Хотя я бы не стал рекомендовать это делать так. Я хотел бы проверить DriverSnapshotHandler с помощью инъекции зависимостей:

public class DriverSnapshotHandler { 

    private FormatterService formatterService; 

    public DriverSnapshotHandler(FormatterService formatterService) { 
     this.formatterService = formatterService; 
    } 

    public String getImageURL() { 
     return formatterService.formatTachoIcon(); 
    } 

} 

Тест блока:

public class TestDriverSnapshotHandler { 

    private FormatterService formatter; 

    @Before 
    public void setUp() { 

     formatter = mock(FormatterService.class); 

     when(formatter.formatTachoIcon()).thenReturn("MockedURL"); 

    } 

    @Test 
    public void testFormatterServiceIsCalled() { 

     DriverSnapshotHandler handler = new DriverSnapshotHandler(formatter); 
     handler.getImageURL(); 

     verify(formatter, times(1)).formatTachoIcon(); 

    } 

} 

Вы можете установить макет в нуль в методе @After. Это решение imho для чистых.

+1

Вам нужно удалить строку 'when (FormatterService.getInstance()). ThenReturn (formatter)', иначе тест не будет запущен – noscreenname

+0

Конечно - это была опечатка; o) –