1. Обзор
В этом руководстве мы рассмотрим следующие аннотации библиотеки Mockito: @Mock
, @Spy
, @Captor
и @InjectMocks
.
Чтобы узнать больше о Mockito, посмотрите серию здесь .
2. Включить аннотации Mockito
Прежде чем идти дальше, давайте рассмотрим различные способы использования аннотаций с тестами Mockito.
2.1. MockitoJUnitRunner
Первый вариант, который у нас есть, — аннотировать тест JUnit с помощью MockitoJUnitRunner
:
@RunWith(MockitoJUnitRunner.class)
public class MockitoAnnotationTest {
...
}
2.2. MockitoAnnotations.initMocks()
Кроме того, мы можем включить аннотации Mockito программно , вызвав MockitoAnnotations.initMocks()
:
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
2.3. MockitoJUnit.rule()
Наконец, мы можем использовать MockitoJUnit.rule()
:
public class MockitoInitWithMockitoJUnitRuleUnitTest {
@Rule
public MockitoRule initRule = MockitoJUnit.rule();
...
}
В этом случае мы должны не забыть сделать наше правило общедоступным
.
3. Аннотация @Mock
Наиболее широко используемая аннотация в Mockito — @Mock
. Мы можем использовать @Mock
для создания и внедрения фиктивных экземпляров без необходимости вызывать Mockito.mock
вручную.
В следующем примере мы создадим макет ArrayList
вручную, не используя аннотацию @Mock
:
@Test
public void whenNotUseMockAnnotation_thenCorrect() {
List mockList = Mockito.mock(ArrayList.class);
mockList.add("one");
Mockito.verify(mockList).add("one");
assertEquals(0, mockList.size());
Mockito.when(mockList.size()).thenReturn(100);
assertEquals(100, mockList.size());
}
Теперь сделаем то же самое, но внедрим макет с помощью аннотации @Mock
:
@Mock
List<String> mockedList;
@Test
public void whenUseMockAnnotation_thenMockIsInjected() {
mockedList.add("one");
Mockito.verify(mockedList).add("one");
assertEquals(0, mockedList.size());
Mockito.when(mockedList.size()).thenReturn(100);
assertEquals(100, mockedList.size());
}
Обратите внимание, как в обоих примерах мы взаимодействуем с макетом и проверяем некоторые из этих взаимодействий, просто чтобы убедиться, что макет ведет себя правильно.
4. @Шпионская
аннотация
Теперь давайте посмотрим, как использовать аннотацию @Spy
для слежки за существующим экземпляром.
В следующем примере мы создаем шпион списка
без использования аннотации @Spy
:
@Test
public void whenNotUseSpyAnnotation_thenCorrect() {
List<String> spyList = Mockito.spy(new ArrayList<String>());
spyList.add("one");
spyList.add("two");
Mockito.verify(spyList).add("one");
Mockito.verify(spyList).add("two");
assertEquals(2, spyList.size());
Mockito.doReturn(100).when(spyList).size();
assertEquals(100, spyList.size());
}
Теперь сделаем то же самое, подсмотрим список, но воспользуемся аннотацией @Spy
:
@Spy
List<String> spiedList = new ArrayList<String>();
@Test
public void whenUseSpyAnnotation_thenSpyIsInjectedCorrectly() {
spiedList.add("one");
spiedList.add("two");
Mockito.verify(spiedList).add("one");
Mockito.verify(spiedList).add("two");
assertEquals(2, spiedList.size());
Mockito.doReturn(100).when(spiedList).size();
assertEquals(100, spiedList.size());
}
Обратите внимание, как, как и раньше, здесь мы взаимодействуем со шпионом, чтобы убедиться, что он ведет себя правильно. В этом примере мы:
- Использовал реальный метод
spiedList.add()
для добавления элементов вspiedList
. - Заглушил метод
spiedList.size()
, чтобы он возвращал100
вместо2
, используяMockito.doReturn()
.
5. @Captor
Аннотация
Далее давайте посмотрим, как использовать аннотацию @Captor
для создания экземпляра ArgumentCaptor .
В следующем примере мы создадим ArgumentCaptor
без использования аннотации @Captor
:
@Test
public void whenNotUseCaptorAnnotation_thenCorrect() {
List mockList = Mockito.mock(List.class);
ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);
mockList.add("one");
Mockito.verify(mockList).add(arg.capture());
assertEquals("one", arg.getValue());
}
Теперь давайте воспользуемся @Captor
для той же цели, чтобы создать экземпляр ArgumentCaptor :
@Mock
List mockedList;
@Captor
ArgumentCaptor argCaptor;
@Test
public void whenUseCaptorAnnotation_thenTheSam() {
mockedList.add("one");
Mockito.verify(mockedList).add(argCaptor.capture());
assertEquals("one", argCaptor.getValue());
}
Обратите внимание, как тест становится проще и читабельнее, когда мы убираем логику конфигурации.
6. @InjectMocks
Аннотация
Теперь давайте обсудим, как использовать аннотацию @InjectMocks
для автоматического внедрения фиктивных полей в тестируемый объект.
В следующем примере мы будем использовать @InjectMocks
для вставки макета wordMap
в MyDictionary
dic
:
@Mock
Map<String, String> wordMap;
@InjectMocks
MyDictionary dic = new MyDictionary();
@Test
public void whenUseInjectMocksAnnotation_thenCorrect() {
Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");
assertEquals("aMeaning", dic.getMeaning("aWord"));
}
Вот класс MyDictionary
:
public class MyDictionary {
Map<String, String> wordMap;
public MyDictionary() {
wordMap = new HashMap<String, String>();
}
public void add(final String word, final String meaning) {
wordMap.put(word, meaning);
}
public String getMeaning(final String word) {
return wordMap.get(word);
}
}
7. Внедрение макета в шпиона
Как и в приведенном выше тесте, мы можем захотеть внедрить макет в шпиона:
@Mock
Map<String, String> wordMap;
@Spy
MyDictionary spyDic = new MyDictionary();
Однако Mockito не поддерживает внедрение моков в шпионов, и следующий тест приводит к исключению:
@Test
public void whenUseInjectMocksAnnotation_thenCorrect() {
Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");
assertEquals("aMeaning", spyDic.getMeaning("aWord"));
}
Если мы хотим использовать макет со шпионом, мы можем вручную внедрить макет через конструктор:
MyDictionary(Map<String, String> wordMap) {
this.wordMap = wordMap;
}
Вместо использования аннотации теперь мы можем создать шпиона вручную:
@Mock
Map<String, String> wordMap;
MyDictionary spyDic;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
spyDic = Mockito.spy(new MyDictionary(wordMap));
}
Теперь тест будет пройден.
8. Столкновение с NPE при использовании аннотации ``
Часто мы можем столкнуться с NullPointerException
, когда пытаемся использовать экземпляр, аннотированный с помощью @Mock
или @Spy
:
public class MockitoAnnotationsUninitializedUnitTest {
@Mock
List<String> mockedList;
@Test(expected = NullPointerException.class)
public void whenMockitoAnnotationsUninitialized_thenNPEThrown() {
Mockito.when(mockedList.size()).thenReturn(1);
}
}
В большинстве случаев это происходит просто потому, что мы забываем правильно включить аннотации Mockito.
Поэтому мы должны помнить, что каждый раз, когда мы хотим использовать аннотации Mockito, мы должны сделать дополнительный шаг и инициализировать их, как мы уже объясняли ранее .
9. Примечания
Наконец, вот несколько заметок об аннотациях Mockito:
- Аннотации Mockito минимизируют повторяющийся код создания макета.
- Они делают тесты более читабельными.
@InjectMocks
необходим для внедрения экземпляров@Spy
и@Mock
.
10. Заключение
В этой небольшой статье мы объяснили основы аннотаций в библиотеке Mockito .
Реализацию всех этих примеров можно найти на GitHub . Это проект Maven, поэтому его должно быть легко импортировать и запускать как есть.
Конечно, чтобы узнать больше о Mockito, посмотрите серию здесь .