Перейти к основному содержимому

Начало работы с Mockito @Mock, @Spy, @Captor и @InjectMocks

· 5 мин. чтения

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, посмотрите серию здесь .