JavaRush /Курсы /JSP & Servlets /Основные аннотации Mockito

Основные аннотации Mockito

JSP & Servlets
4 уровень , 1 лекция
Открыта

2.1 Аннотация @Mock

Есть два способа работы с мок-объектами в Mockito. Первый – это создать полностью виртуальный объект, второй – это обернуть существующий объект в некую обертку. Начнем с первого.

Чтобы создать полностью виртуальный объект, нужно написать код:

ИмяКласса имяПеременной = Mockito.mock(ИмяКласса.class);

Давайте ради примера создадим мок класса ArrayList:


@ExtendWith(MockitoExtension.class)
class MockTest {
 @Test
    public void whenNotUseMockAnnotation_thenCorrect() {
 List mockList = Mockito.mock(ArrayList.class);
        //эти методы не будут ничего делать – это заглушки
 mockList.add("one");  
 mockList.add("two");
    }
}

В этом примере мы создаем фейковый объект типа ArrayList и сохраняем ссылку на него в переменную mockList. Методы этого объекта ничего не делают.

Кстати, этот код можно записать еще короче, так как для этого есть специальная аннотация @Mock.


@ExtendWith(MockitoExtension.class)
class MockTest {
 @Mock
 List mockList;
 
 @Test
    public void whenNotUseMockAnnotation_thenCorrect() {
        //эти методы не будут ничего делать – это заглушки
 mockList.add("one");  
 mockList.add("two");
    }
}

Во втором случае MockitoExtension сам проанализирует код класса и создаст нужные заглушки. Вызывать метод Mockito.mock() не нужно. Одна аннотация и виртуальный объект готов. Красота.

2.2 Аннотация @Spy

Второй важный тип объектов в Mockito – это обертки над существующими объектами. Они позволяют с одной стороны пользоваться уже существующими классами, а с другой – перехватывать обращение ко всем методам и переменным таких объектов: подкорректировать их работу, где это нужно. Используются так же часто, как и Mock-объекты.

Чтобы создать обертку над объектом, нужно написать код:

ИмяКласса имяПеременной = Mockito.spy(объект);

Пример с оберткой вокруг класса ArrayList:


@ExtendWith(MockitoExtension.class)
class SpyTest {
 @Test
    public void whenMockAnnotation() {
 List<String> mockList = Mockito.spy(new ArrayList<String>()); //эти методы будут работать!  mockList.add("one");  mockList.add("two"); } } 

В самом простом варианте обращение к объекту-обертке просто перенаправляет вызовы к оригинальному объекту, ссылку на который он хранит у себя внутри. Все будет работать, как и с оригинальным объектом.

Создать обертку так же можно с помощью аннотации – @Spy.


@ExtendWith(MockitoExtension.class)
class SpyTest {
 @Spy
 List mockList = new ArrayList<String>();
 
 @Test
    public void whenMockAnnotation() {
        // эти методы будут работать!
 mockList.add("one");  
 mockList.add("two");
    }
}

Эти два примера кода эквиваленты.

Комментарии (19)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Андрей Уровень 37 Expert
29 июля 2024
Подчеркну пару моментов: 1. Если инициализируете stub-объекты(mock, spy) напрямую через Mokito.mock... то MockitoExtension можно не подключать. Если используете аннотации - то подключение обязательное 2. Если spy не инициализировать ни чем - то он ведет себя как обычный mock
Murat Уровень 51
7 октября 2023
А как в примере с аннотацией @Mock JUnit понимает, что мы работает с ArrayList? И почему тип List не параметризован? Как компилятор понимает какой тип будет храниться в List'е?

@ExtendWith(MockitoExtension.class)
class MockTest {
    @Mock
    List mockList;

    @Test
    public void whenNotUseMockAnnotation_thenCorrect() {
        //эти методы не будут ничего делать – это заглушки
        mockList.add("one");
        mockList.add("two");
    }
}
Андрей Уровень 109
15 апреля 2024
В данном случае реализация не важна, т. к. ее просто нет (Mock генерирует заглушки), важны только набор методов, который и предоставляет интерфейс List. А List бывает и непараметризованным, это называется "сырой" тип.
Anonymous #3322801 Уровень 2 Expert
28 июля 2023
У кого вылетает ошибка:

org.mockito.exceptions.base.MockitoException: Unable to initialize @Spy annotated field 'mockListCats'.
Could not initialize plugin: interface org.mockito.plugins.MockMaker (alternate: null)
попробуйте добавить в помник:

            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.1</version>
                <configuration>
                    <argLine>-Djdk.attach.allowAttachSelf=true </argLine>
                </configuration>
            </plugin>
31 мая 2023
Те примеры, которые привели в разделе @Spy, не работают, у меня заработал только следующий код, когда инициализация переменной происходит перед каждым методом.

package com.konovalov.service;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.ArrayList;
import java.util.List;

@ExtendWith(MockitoExtension.class)
class MainTest {
    @Spy
    List<String> mockList;

    @BeforeEach
    public void mockPrepare(){
        mockList = new ArrayList<String>(2);
    }
    @Test
    public void whenMockAnnotation() {

        //эти методы будут работать!
        mockList.add("one");
        mockList.add("two");
        System.out.println(mockList.size());
    }
}




Караганов Михаил Уровень 108 Expert
29 августа 2022
самом простом варианте обращение к объекту-обертке просто перенаправляет вызовы к оригинальному объекту, ссылку на который он хранит у себя внутри. Все будет работать, как и с оригинальным объектом." А в более сложном варианте? И если вызовы перенаправляются к оригиналу, то зачем обертка, раз методы отрабатывают точно так же?
Justinian Уровень 41 Master
28 августа 2023
Хотел пример привести, браузер все съел, неудобно конечно. Ладно, по простому, смотрим документацию @Spy

Creates a spy of the real object. The spy calls real methods unless they are stubbed
Итого у нас три основных варианта: 1. @Mock используем когда нам нужно замокать внешние зависимости класса (например в сервисе используются бины репозитория и другие сервисы), это необходимо для написания юнит тестов, ведь мы тестируем один конкретный метод, а не всю программу сразу со всеми классами. 2. Без аннотаций: SomeClass some = new SomeClass(); так мы делаем, если класс SomeClass отработает корректно при таком простом вызове (то есть ему не нужен специальный и заморочный контекст, у него под капотом нету зависимостей на другие классы и тд), так обычно используют если SomeClass простой как двери, не использует никаких внешних классов, а значит его код нормально отработает при тестах. хотя, если и есть зависимости, то можно и их замокать, но это уже чуть более сложный кейс и другой разговор, здесь же суть в том, что во время нашего теста, SomeClass просто отработает и все. 3. @Spy же сочетает в себе пункт 1 и пункт 2. Он реально отработает. Но при этом мы можем использовать интерфейс Мокито при работе с ним: мы можем переопределять поведение, делать стабы вызовов методов - то есть когда нам нужно, вместо реальных вызовов методов, возвращать то, что мы скажем. И при этом можем использовать verify для проверки, было ли взаимодействие с нужным методом или нет. При пункте 2 (без Spy) мы этого сделать не можем - проверить вызвался нужный метод или нет (при помощи Mockito.verify) Также @Spy позволяет удобнее инициализировать объекты: @Mock SomeBean someBean; @Spy SomeClass someClass; @InjectMocks TestedClass sut; Если мы уберем аннотацию @Spy мы не сможем использовать @InjectMocks, поскольку в поле класса вместо Spy или реального инстанса пойдет null.
Алексей Уровень 91 Expert
25 июля 2022

@Mock
    List mockList;
Когда мы пишем так, какой тип List и с каким дженериком создает Mockito?
On1k Уровень 45
6 августа 2022
Может эквивалентно второй половине? new ArrayList<String>();
Алексей Уровень 91 Expert
7 августа 2022
Вопрос про те примере где нет второй половины... В примерах задано объявление с аннотацией и потом сразу метод add
Justinian Уровень 41 Master
9 августа 2022
Относительно дженерика, у нас непараметризированная коллекция, поэтому там Object ы будут, все остальное через правила кастинга. Относительно типа List, а зачем нам тип List? Мокито создаст свой чайлд класс, который заэкстендит от List и имплементирует соответствующие методы интерфейса. Мы ведь все-равно другие методы не сможем вызывать кроме тех, которые в List. Если нам нужна конкретная коллекция, то будем мокать конкретную.
21 марта 2023
вроде как получается raw type (все объекты коллекции кастятся в Object)
Иван Голубев Уровень 108 Expert
9 июля 2022
для junit 5 @ExtendWith(MockitoExtension.class) вместо @RunWith(MockitoJUnitRunner.class)
Булат Уровень 1 Expert
8 июля 2022
@Spy List mockList = new ArrayList<String>(); Здесь же создается новый объект, где подмена?
Anton Nikolaev Уровень 108 Expert
20 июля 2022
Аннотация Spy над созданным объектом коллекции говорит о том, что Mokito контролирует его вызовы с помощью рефлексии и работает как прокси.
Максим Уровень 108
21 июня 2022
List mockList = new ArrayList<String>(); Пропустили в начале дженерик
Максим Уровень 108
21 июля 2022
Тогда нужно убрать от сюда "<String>"

 new ArrayList<String>()
Максим Уровень 108
21 июля 2022
Да, ты прав, я всегда думал что есть только 2 вида записи

List<String> mockList = new ArrayList<>();
List<String> mockList = new ArrayList<String>();
Александр Огарков Уровень 4 Expert
23 июля 2022
В первом случае получишь сырой тип.