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

Mockito — использование шпионов

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

1. Обзор

В этом уроке мы покажем, как максимально эффективно использовать шпионов в Mockito .

Мы поговорим об аннотации @Spy и о том, как заглушить шпиона. Наконец, мы рассмотрим разницу между Mock и Spy .

Конечно, чтобы узнать больше о Mockito, посмотрите серию здесь .

2. Простой шпионский пример

Начнем с простого примера использования шпиона .

Проще говоря, API — это Mockito.spy() для слежки за реальным объектом .

Это позволит нам вызывать все обычные методы объекта, продолжая отслеживать каждое взаимодействие, как если бы мы использовали мокап.

Теперь давайте сделаем быстрый пример, где мы будем следить за существующим объектом ArrayList :

@Test
public void whenSpyingOnList_thenCorrect() {
List<String> list = new ArrayList<String>();
List<String> spyList = Mockito.spy(list);

spyList.add("one");
spyList.add("two");

Mockito.verify(spyList).add("one");
Mockito.verify(spyList).add("two");

assertEquals(2, spyList.size());
}

Обратите внимание, как на самом деле вызывается реальный метод add() и как размер spyList становится равным 2.

3. Аннотация @Spy

Далее давайте посмотрим, как использовать аннотацию @Spy . Мы можем использовать аннотацию @Spy вместо spy() :

@Spy
List<String> spyList = new ArrayList<String>();

@Test
public void whenUsingTheSpyAnnotation_thenObjectIsSpied() {
spyList.add("one");
spyList.add("two");

Mockito.verify(spyList).add("one");
Mockito.verify(spyList).add("two");

assertEquals(2, spyList.size());
}

Чтобы включить аннотации Mockito (такие как @Spy , @Mock , …), нам нужно сделать одно из следующих действий:

  • Вызовите метод MockitoAnnotations.initMocks(this) для инициализации аннотированных полей .
  • Используйте встроенный бегун @RunWith(MockitoJUnitRunner.class)

4. Убить шпиона

Теперь давайте посмотрим, как заглушить шпиона . Мы можем настроить/переопределить поведение метода, используя тот же синтаксис, который мы использовали бы с макетом.

Здесь мы будем использовать doReturn() для переопределения метода size() :

@Test
public void whenStubASpy_thenStubbed() {
List<String> list = new ArrayList<String>();
List<String> spyList = Mockito.spy(list);

assertEquals(0, spyList.size());

Mockito.doReturn(100).when(spyList).size();
assertEquals(100, spyList.size());
}

5. Мок против шпиона в Mockito

Давайте обсудим разницу между Mock и Spy в Mockito. Мы не будем рассматривать теоретические различия между двумя концепциями, а только то, как они различаются внутри самого Mockito.

Когда Mockito создает макет, он делает это из класса типа, а не из фактического экземпляра. Макет просто создает простой экземпляр оболочки класса, полностью приспособленный для отслеживания взаимодействий с ним.

С другой стороны, шпион будет обертывать существующий экземпляр . Он по-прежнему будет вести себя так же, как и обычный экземпляр, с той лишь разницей, что он также будет оснащен инструментами для отслеживания всех взаимодействий с ним.

Здесь мы создадим макет класса ArrayList :

@Test
public void whenCreateMock_thenCreated() {
List mockedList = Mockito.mock(ArrayList.class);

mockedList.add("one");
Mockito.verify(mockedList).add("one");

assertEquals(0, mockedList.size());
}

Как мы видим, добавление элемента в фиктивный список на самом деле ничего не добавляет, он просто вызывает метод без каких-либо других побочных эффектов.

Шпион, с другой стороны, будет вести себя иначе; на самом деле он вызовет реальную реализацию метода добавления и добавит элемент в базовый список:

@Test
public void whenCreateSpy_thenCreate() {
List spyList = Mockito.spy(new ArrayList());

spyList.add("one");
Mockito.verify(spyList).add("one");

assertEquals(1, spyList.size());
}

6. Понимание Mockito NotAMockException

В этом последнем разделе мы узнаем об исключении Mockito NotAMockException . Это исключение является одним из распространенных исключений, с которыми мы, вероятно, столкнемся при неправильном использовании моков или шпионов .

Начнем с понимания обстоятельств, при которых может возникнуть это исключение:

List<String> list = new ArrayList<String>();
Mockito.doReturn(100).when(list).size();

assertEquals("Size should be 100: ", 100, list.size());

Когда мы запустим этот фрагмент кода, мы получим следующую ошибку:

org.mockito.exceptions.misusing.NotAMockException: 
Argument passed to when() is not a mock!
Example of correct stubbing:
doThrow(new RuntimeException()).when(mock).someMethod();

К счастью, из сообщения об ошибке Mockito совершенно ясно, в чем здесь проблема. В нашем примере объект списка не является макетом. Метод Mockito when() ожидает в качестве аргумента фиктивный или шпионский объект .

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

final List<String> spyList = Mockito.spy(new ArrayList<String>());
Mockito.doReturn(100).when(spyList).size();

assertEquals("Size should be 100: ", 100, spyList.size());

Наш пример теперь ведет себя так, как ожидалось, и мы больше не видим исключение Mockito NotAMockException.

7. Заключение

В этой небольшой статье мы обсудили наиболее полезные примеры использования шпионов Mockito.

Мы узнали, как создать шпиона , как использовать аннотацию @Spy , как заглушить шпиона и, наконец, разницу между Mock и Spy .

Реализацию всех этих примеров можно найти на GitHub .

Это проект Maven, поэтому его должно быть легко импортировать и запускать как есть.

Наконец, чтобы узнать больше о Mockito, посмотрите серию здесь .