Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Что такое Mock
Mock — это имитация (подделка) объекта, которая используется в тестировании. Вместо реального объекта ты создаёшь фальшивый, который ведёт себя так, как тебе нужно для теста.
Основная идея
Вместо: реальная БД → HTTP запросы → файлы → кеш
Мок: имитирует ответ, не обращаясь к реальной системе
Зачем нужны моки
1. Изолировать код
public class UserService {
private UserRepository repository; // Это то, что мокируем
public UserService(UserRepository repository) {
this.repository = repository;
}
public User getUserById(Long id) {
return repository.findById(id);
}
}
// Без мока:
UserService service = new UserService(new RealDatabase());
// Тест медленный (подключение к БД), зависит от БД, может упасть
// С моком:
UserRepository mockRepo = Mockito.mock(UserRepository.class);
UserService service = new UserService(mockRepo);
// Тест быстрый, независимый от БД, предсказуемый
2. Тестировать сложное взаимодействие
public class PaymentService {
private PaymentGateway gateway; // Внешний API
private UserRepository userRepo;
private EmailService emailService;
public void processPayment(User user, Order order) {
// Нельзя вызвать реальный API в тестах!
gateway.charge(order.getAmount());
userRepo.updateBalance(user.getId());
emailService.sendReceipt(user.getEmail());
}
}
// С моками:
PaymentGateway mockGateway = Mockito.mock(PaymentGateway.class);
UserRepository mockRepo = Mockito.mock(UserRepository.class);
EmailService mockEmail = Mockito.mock(EmailService.class);
PaymentService service = new PaymentService(mockGateway, mockRepo, mockEmail);
// Теперь тестируем логику без реальных вызовов
Типы test doubles
1. Mock (проверяем вызовы)
// Mock проверяет, что нужные методы были вызваны
UserRepository mockRepo = Mockito.mock(UserRepository.class);
UserService service = new UserService(mockRepo);
service.registerUser(new User("John"));
// Проверяем, что save был вызван
Mockito.verify(mockRepo).save(Mockito.any(User.class));
2. Stub (возвращаем фиксированные значения)
// Stub возвращает заранее подготовленные данные
UserRepository stubRepo = Mockito.mock(UserRepository.class);
User testUser = new User("John");
// Когда вызывают findById(1), возвращаем testUser
Mockito.when(stubRepo.findById(1L)).thenReturn(testUser);
UserService service = new UserService(stubRepo);
User result = service.getUserById(1L);
assertThat(result).isEqualTo(testUser);
3. Spy (частичный mock — оборачиваем реальный объект)
// Spy — это реальный объект, но некоторые методы можно "подменить"
UserRepository realRepo = new RealUserRepository();
UserRepository spyRepo = Mockito.spy(realRepo);
// Этот метод работает как реальный
spyRepo.findAll(); // вызывает БД
// Этот метод мокируем
Mockito.when(spyRepo.findById(999L)).thenReturn(null);
// 999 вернёт null, а остальные IDs будут из реальной БД
Практический пример с Mockito
import org.mockito.Mockito;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
public class UserServiceTest {
@Test
public void testGetUserById() {
// 1. ARRANGE (подготовка)
UserRepository mockRepo = Mockito.mock(UserRepository.class);
User testUser = new User(1L, "John", "john@example.com");
// Говорим моку: когда вызовут findById(1), верни testUser
Mockito.when(mockRepo.findById(1L)).thenReturn(testUser);
UserService service = new UserService(mockRepo);
// 2. ACT (действие)
User result = service.getUserById(1L);
// 3. ASSERT (проверка)
assertThat(result).isEqualTo(testUser);
assertThat(result.getName()).isEqualTo("John");
}
@Test
public void testRegisterUser() {
// ARRANGE
UserRepository mockRepo = Mockito.mock(UserRepository.class);
UserService service = new UserService(mockRepo);
User newUser = new User(null, "Jane", "jane@example.com");
// ACT
service.registerUser(newUser);
// ASSERT: проверяем, что save был вызван с нужным user
Mockito.verify(mockRepo).save(newUser);
// Или более гибко:
Mockito.verify(mockRepo).save(Mockito.argThat(
user -> "jane@example.com".equals(user.getEmail())
));
}
@Test
public void testUserNotFound() {
// ARRANGE
UserRepository mockRepo = Mockito.mock(UserRepository.class);
Mockito.when(mockRepo.findById(999L)).thenReturn(null);
UserService service = new UserService(mockRepo);
// ACT
User result = service.getUserById(999L);
// ASSERT
assertThat(result).isNull();
}
@Test
public void testExceptionHandling() {
// ARRANGE
UserRepository mockRepo = Mockito.mock(UserRepository.class);
Mockito.when(mockRepo.findById(Mockito.any()))
.thenThrow(new DatabaseException("Connection failed"));
UserService service = new UserService(mockRepo);
// ACT & ASSERT
assertThatThrownBy(() -> service.getUserById(1L))
.isInstanceOf(DatabaseException.class)
.hasMessage("Connection failed");
}
}
Аннотации Mockito
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;
@ExtendWith(MockitoExtension.class) // Активирует @Mock и @InjectMocks
public class UserServiceTest {
@Mock
private UserRepository mockRepo; // Автоматически создаёт mock
@Mock
private EmailService mockEmail;
@InjectMocks
private UserService service; // Автоматически внедряет моки в service
@Test
public void testRegister() {
// mockRepo и mockEmail уже готовы
// service уже получил их в конструктор/setter
Mockito.when(mockRepo.save(Mockito.any())).thenReturn(true);
boolean result = service.registerUser(new User());
assertThat(result).isTrue();
Mockito.verify(mockEmail).sendWelcomeEmail(Mockito.any());
}
}
Частые случаи использования
1. Задать возвращаемое значение
Mockito.when(mockRepo.findById(1L)).thenReturn(user);
Mockito.when(mockRepo.findAll()).thenReturn(List.of(user1, user2));
2. Выбросить исключение
Mockito.when(mockRepo.save(Mockito.any()))
.thenThrow(new DatabaseException());
3. Вернуть разные значения при разных вызовах
Mockito.when(mockRepo.findById(1L))
.thenReturn(user1) // Первый вызов
.thenReturn(user2) // Второй вызов
.thenThrow(new Exception()); // Третий вызов
4. Проверить, сколько раз был вызван метод
Mockito.verify(mockRepo, Mockito.times(2)).findById(1L);
Mockito.verify(mockRepo, Mockito.never()).deleteById(1L);
Mockito.verify(mockRepo, Mockito.atLeast(1)).save(Mockito.any());
5. Проверить аргументы
// Точное совпадение
Mockito.verify(mockEmail).sendEmail("user@example.com");
// С любым аргументом
Mockito.verify(mockRepo).save(Mockito.any(User.class));
// С условием
Mockito.verify(mockRepo).save(
Mockito.argThat(user -> user.getEmail().contains("@"))
);
Mock vs Stub vs Real (когда что использовать)
| Случай | Выбор | Пример |
|---|---|---|
| Проверяем вызовы методов | Mock | verify(mock).method() |
| Нужны фиксированные ответы | Stub | when(stub.method()).thenReturn(value) |
| Нужно реальное поведение | Real object | Используем реальный класс |
| Нужно 90% реального + 10% мока | Spy | spy(realObject) |
Когда НЕ использовать моки
❌ Для тестирования utility функций (без dependencies) ❌ Для всего подряд (замедляет тесты и усложняет) ❌ Вместо правильной архитектуры (если много моков = плохой дизайн)
Best practices
// ✅ ХОРОШО: мокируем только зависимости
@Test
public void testBusinessLogic() {
UserRepository mockRepo = Mockito.mock(UserRepository.class);
User user = new User(); // реальный объект, не мок
service.process(user);
}
// ❌ ПЛОХО: мокируем всё
@Test
public void testBusinessLogic() {
User mockUser = Mockito.mock(User.class);
UserRepository mockRepo = Mockito.mock(UserRepository.class);
// Теперь тестируем тесты, а не код
}
Итог
Mock — это подставной объект, который:
- Имитирует поведение реального объекта
- Позволяет тестировать в изоляции (без БД, API, файлов)
- Позволяет проверить, были ли вызваны нужные методы
- Делает тесты быстрыми и предсказуемыми
Используй моки для зависимостей, но не переусложняй архитектуру.