Първи стъпки с Mockito @Mock, @Spy, @Captor и @InjectMocks

1. Общ преглед

В този урок ще разгледаме анотациите на библиотеката Mockito - @Mock , @Spy , @Captor и @InjectMocks .

За повече доброта на Mockito, погледнете серията тук.

2. Активирайте Mockito Annotations

Преди да продължим по-нататък, нека разгледаме различни начини да разрешим използването на анотации с тестове 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 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 анотация, за да шпионираме съществуващ екземпляр.

В следващия пример - ние създаваме шпионин на Списък със стария начин, без да използваме @Spy анотация:

@Test public void whenNotUseSpyAnnotation_thenCorrect() { List spyList = Mockito.spy(new ArrayList()); 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 spiedList = new ArrayList(); @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 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 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 wordMap; public MyDictionary() { wordMap = new HashMap(); } 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 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 wordMap) { this.wordMap = wordMap; }

Вместо да използваме анотацията, вече можем да създадем ръчно шпионката:

@Mock Map 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 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, разгледайте поредицата тук.