← Назад к вопросам

Как работает Mockito и для чего его используют?

1.0 Junior🔥 161 комментариев
#Тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Mockito: Фреймворк для Моков в Тестировании

Mockito — это популярный фреймворк для создания mock-объектов (подделок) в unit-тестах на Java. Он позволяет изолировать тестируемый код от его зависимостей, обеспечивая контролируемую и предсказуемую среду тестирования.

Назначение Mockito

Основные задачи:

  • Изоляция кода: тестировать логику в отрыве от внешних зависимостей
  • Контроль поведения: определять, что должны возвращать зависимости
  • Проверка взаимодействия: убедиться, что методы вызывались правильно
  • Упрощение тестов: избежать сложной инициализации реальных объектов
  • Скорость тестирования: мокированные объекты работают быстрее реальных

Основные Концепции

1. Mock объекты Подделка реального объекта, которая отслеживает вызовы методов и возвращает заранее определённые значения.

2. Stub (заглушка) Мок, который просто возвращает фиксированное значение.

3. Spy (шпион) Частичный мок реального объекта, который следит за вызовами но сохраняет оригинальное поведение.

Основное Использование

Создание простого мока:

import static org.mockito.Mockito.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class OrderServiceTest {
    
    @Test
    public void testOrder() {
        // Создаём мок
        PaymentService paymentService = mock(PaymentService.class);
        
        // Настраиваем поведение
        when(paymentService.charge(100)).thenReturn(true);
        
        // Используем в коде
        OrderService orderService = new OrderService(paymentService);
        boolean result = orderService.placeOrder(100);
        
        // Проверяем результат
        assertTrue(result);
        
        // Проверяем, что метод был вызван
        verify(paymentService).charge(100);
    }
}

Настройка Поведения Мока

1. Возвращаемые значения (Stubbing)

public class UserServiceTest {
    @Test
    public void testFindUser() {
        UserRepository repo = mock(UserRepository.class);
        User user = new User(1, "Alice");
        
        // Простой stub
        when(repo.findById(1)).thenReturn(user);
        
        // Несколько вызовов
        when(repo.findById(2))
            .thenReturn(new User(2, "Bob"))
            .thenReturn(new User(2, "Bob Updated"));
        
        // Исключения
        when(repo.findById(999)).thenThrow(new NotFoundException());
        
        UserService service = new UserService(repo);
        assertEquals("Alice", service.getUserName(1));
    }
}

2. Аргументные Матчеры (Argument Matchers)

@Test
public void testWithMatchers() {
    PaymentService payment = mock(PaymentService.class);
    
    // Любое целое число
    when(payment.charge(anyInt())).thenReturn(true);
    
    // Любая строка
    when(payment.refund(anyString())).thenReturn(true);
    
    // Конкретное значение
    when(payment.charge(eq(100))).thenReturn(true);
    
    // Проверка диапазона
    when(payment.charge(gt(50))).thenReturn(true);
}

Проверка Вызовов (Verify)

1. Базовая проверка

@Test
public void testVerifyCall() {
    Logger logger = mock(Logger.class);
    ErrorHandler handler = new ErrorHandler(logger);
    
    handler.handleError("Critical error");
    
    // Проверяем, что метод был вызван
    verify(logger).error("Critical error");
}

2. Количество вызовов

@Test
public void testVerifyTimes() {
    Database db = mock(Database.class);
    Cache cache = new Cache(db);
    
    cache.get("key1");
    cache.get("key1");  // не должна обращаться к БД
    
    verify(db, times(1)).query("key1");  // только один раз
    verify(db, never()).query("key2");
    verify(db, atLeast(1)).query("key1");
    verify(db, atMost(1)).query("key1");
}

3. Порядок вызовов

@Test
public void testCallOrder() {
    PaymentService payment = mock(PaymentService.class);
    NotificationService notify = mock(NotificationService.class);
    
    OrderService service = new OrderService(payment, notify);
    service.processOrder(100);
    
    // Проверяем порядок
    InOrder inOrder = inOrder(payment, notify);
    inOrder.verify(payment).charge(100);
    inOrder.verify(notify).sendConfirmation();
}

Spy (Шпион на реальном объекте)

@Test
public void testWithSpy() {
    // Реальный список
    List<String> realList = new ArrayList<>();
    List<String> spyList = spy(realList);
    
    // Подменяем только определённый метод
    when(spyList.size()).thenReturn(100);
    
    // Остальное работает как обычно
    spyList.add("item1");
    assertEquals(1, realList.size());
    assertEquals(100, spyList.size());  // мокированный метод
    
    // Можно проверять реальные вызовы
    verify(spyList).add("item1");
}

Аннотации (Annotation-Based)

import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(MockitoExtension.class)
public class ServiceTest {
    
    @Mock
    private UserRepository userRepo;
    
    @Mock
    private EmailService emailService;
    
    @InjectMocks
    private UserService userService;  // автоматически инъектит моки
    
    @Test
    public void test() {
        when(userRepo.findById(1)).thenReturn(new User(1, "John"));
        // userService уже инициализирована с мокированным userRepo
        User user = userService.getUser(1);
        assertEquals("John", user.getName());
    }
}

Реальный Пример: Тестирование Сервиса Заказов

@ExtendWith(MockitoExtension.class)
public class OrderServiceTest {
    
    @Mock
    private PaymentService paymentService;
    @Mock
    private InventoryService inventoryService;
    @Mock
    private NotificationService notificationService;
    
    @InjectMocks
    private OrderService orderService;
    
    @Test
    public void testSuccessfulOrder() {
        // Подготовка
        when(inventoryService.isAvailable("ITEM-123")).thenReturn(true);
        when(paymentService.charge(150)).thenReturn(true);
        
        // Выполнение
        Order order = new Order("ITEM-123", 150);
        boolean result = orderService.placeOrder(order);
        
        // Проверка
        assertTrue(result);
        verify(paymentService).charge(150);
        verify(inventoryService).reserve("ITEM-123");
        verify(notificationService).sendConfirmation(any());
    }
    
    @Test
    public void testOrderFailsWhenOutOfStock() {
        when(inventoryService.isAvailable("ITEM-123")).thenReturn(false);
        
        Order order = new Order("ITEM-123", 150);
        boolean result = orderService.placeOrder(order);
        
        assertFalse(result);
        verify(paymentService, never()).charge(anyInt());
        verify(notificationService).sendNotAvailable("ITEM-123");
    }
}

Лучшие Практики

  1. Мокируй только зависимости, не тестируемый код
  2. Избегай излишних мокирований: только когда необходимо
  3. Используй Argument Matchers для гибкости
  4. Проверяй поведение, не реализацию: verify что вызовы произошли
  5. Держи тесты простыми: один сценарий на тест
  6. Используй @InjectMocks вместо ручной инъекции

Итоговое Резюме

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

Как работает Mockito и для чего его используют? | PrepBro