← Назад к вопросам
Какие объекты заменял бы Mock в JUnit
2.0 Middle🔥 241 комментариев
#SOLID и паттерны проектирования#Тестирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Mock объекты в JUnit и Mockito
Mock объекты — это поддельные объекты, которые имитируют поведение реальных объектов. Они критичны для unit-тестирования, позволяя нам тестировать код в полной изоляции.
Что такое Mock?
Mock — это объект, который:
- Имитирует поведение реального объекта
- Отслеживает вызовы методов
- Позволяет проверить, как он был использован
- Позволяет задать ожидаемое поведение
Типы тестовых двойников
1. Dummy (Заглушка)
Dummy объект создаётся, но никогда не используется.
public class EmailServiceDummy implements EmailService {
@Override
public void sendEmail(String to, String message) {
// Пусто — это заглушка
}
}
public class NotificationServiceTest {
@Test
void testNotificationCreation() {
EmailService dummy = new EmailServiceDummy();
NotificationService service = new NotificationService(dummy);
Notification notification = service.createNotification("Hello");
assertThat(notification.getMessage()).isEqualTo("Hello");
// dummy не используется в тесте
}
}
2. Stub (Подвал)
Stub — это объект, который возвращает заранее определённые значения.
public class UserRepositoryStub implements UserRepository {
@Override
public User findById(Long id) {
// Всегда возвращает одного и того же пользователя
return new User(1L, "John Doe", "john@example.com");
}
}
public class UserServiceTest {
@Test
void testGetUserById() {
UserRepository stub = new UserRepositoryStub();
UserService service = new UserService(stub);
User user = service.getUserInfo(1L);
assertThat(user.getName()).isEqualTo("John Doe");
}
}
3. Mock (Мок)
Mock — это объект, который:
- Проверяет, как он был вызван
- Может проверить параметры вызовов
- Может проверить количество вызовов
public class OrderServiceTest {
@Test
void testOrderCreationCallsPaymentService() {
// Создаём mock
PaymentService mockPayment = mock(PaymentService.class);
OrderService service = new OrderService(mockPayment);
// Задаём поведение
when(mockPayment.charge(any(BigDecimal.class)))
.thenReturn(true);
// Вызываем сервис
Order order = service.createOrder(new Order(BigDecimal.valueOf(100)));
// Проверяем, как использовался mock
verify(mockPayment, times(1)).charge(BigDecimal.valueOf(100));
}
}
4. Spy (Шпион)
Spy — это частичный mock реального объекта. Он отслеживает вызовы, но вызывает реальные методы.
public class CalculatorSpyTest {
@Test
void testCalculatorWithSpy() {
// Создаём spy на реальный объект
Calculator calculator = spy(new Calculator());
// Задаём поведение для одного метода
when(calculator.add(2, 2)).thenReturn(5); // Переопределяем
// Остальные методы работают реально
assertThat(calculator.add(2, 2)).isEqualTo(5); // mock
assertThat(calculator.subtract(5, 3)).isEqualTo(2); // реально
// Проверяем вызовы
verify(calculator).add(2, 2);
verify(calculator).subtract(5, 3);
}
}
5. Fake (Подделка)
Fake — это рабочая реализация, но упрощённая.
public class InMemoryUserRepository implements UserRepository {
private Map<Long, User> users = new HashMap<>();
@Override
public User findById(Long id) {
return users.get(id);
}
@Override
public void save(User user) {
users.put(user.getId(), user);
}
}
public class UserServiceTest {
@Test
void testGetUserById() {
UserRepository fakeRepo = new InMemoryUserRepository();
fakeRepo.save(new User(1L, "John"));
UserService service = new UserService(fakeRepo);
User user = service.getUserInfo(1L);
assertThat(user.getName()).isEqualTo("John");
}
}
Практические сценарии использования Mock
Сценарий 1: Mock External API
public class PaymentGatewayMockTest {
@Test
void testPaymentProcessing() {
// Мокируем внешний сервис платежей
PaymentGateway mockGateway = mock(PaymentGateway.class);
when(mockGateway.processPayment(any(PaymentRequest.class)))
.thenReturn(new PaymentResponse("SUCCESS", "TXN123"));
PaymentProcessor processor = new PaymentProcessor(mockGateway);
PaymentResponse response = processor.process(new PaymentRequest(100));
assertThat(response.getStatus()).isEqualTo("SUCCESS");
verify(mockGateway, times(1)).processPayment(any());
}
}
Сценарий 2: Mock Database
public class UserServiceDatabaseMockTest {
@Test
void testCreateUserSavesToDatabase() {
// Мокируем БД
UserRepository mockDB = mock(UserRepository.class);
when(mockDB.save(any(User.class)))
.thenAnswer(invocation -> {
User user = invocation.getArgument(0);
user.setId(1L); // Имитируем присвоение ID
return user;
});
UserService service = new UserService(mockDB);
User savedUser = service.createUser("John Doe", "john@example.com");
assertThat(savedUser.getId()).isNotNull();
verify(mockDB).save(any(User.class));
}
}
Сценарий 3: Mock Logger
public class OrderServiceLoggingTest {
@Test
void testOrderServiceLogsEvents() {
// Мокируем logger
Logger mockLogger = mock(Logger.class);
OrderService service = new OrderService(mockLogger);
Order order = new Order(100);
service.processOrder(order);
// Проверяем, что логирование произошло
verify(mockLogger).info("Order created: " + order.getId());
verify(mockLogger).info("Order processed");
}
}
Сценарий 4: Mock Email Service
public class NotificationServiceEmailMockTest {
@Test
void testNotificationSendsEmail() {
EmailService mockEmail = mock(EmailService.class);
NotificationService service = new NotificationService(mockEmail);
service.notifyUser("user@example.com", "Welcome!");
// Проверяем параметры вызова
verify(mockEmail).sendEmail(
eq("user@example.com"),
contains("Welcome")
);
}
}
Сценарий 5: Mock с исключениями
public class ErrorHandlingTest {
@Test
void testHandlesPaymentException() {
PaymentService mockPayment = mock(PaymentService.class);
// Мокируем выброс исключения
when(mockPayment.charge(any()))
.thenThrow(new PaymentException("Network error"));
OrderService service = new OrderService(mockPayment);
assertThatThrownBy(() -> service.processOrder(new Order(100)))
.isInstanceOf(OrderProcessingException.class)
.hasMessageContaining("Payment failed");
}
}
Важные конфигурации Mock с Mockito
public class AdvancedMockitoTest {
@Test
void demonstrateAdvancedMocking() {
// 1. Mock с strict stubs (отслеживает неиспользуемые stubы)
UserRepository mockRepo = mock(UserRepository.class, STRICT_STUBS);
when(mockRepo.findById(1L))
.thenReturn(new User(1L, "John"));
// 2. InOrder verification (проверка порядка вызовов)
UserRepository repo = mock(UserRepository.class);
UserService service = new UserService(repo);
service.updateUser(new User(1L, "Updated"));
service.notifyUser(1L);
InOrder inOrder = inOrder(repo);
inOrder.verify(repo).save(any());
inOrder.verify(repo).findById(1L); // Проверяет, что save был ДО findById
// 3. Verify no more interactions
UserRepository anotherMock = mock(UserRepository.class);
// Что-то делаем
verify(anotherMock).findById(1L);
verifyNoMoreInteractions(anotherMock); // Проверяет нет других вызовов
// 4. ArgumentCaptor (захват аргументов для проверки)
UserRepository captorMock = mock(UserRepository.class);
UserService captorService = new UserService(captorMock);
User newUser = new User(null, "Jane");
captorService.createUser(newUser);
ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);
verify(captorMock).save(captor.capture());
User capturedUser = captor.getValue();
assertThat(capturedUser.getName()).isEqualTo("Jane");
}
}
Когда использовать Mock vs. реальные объекты
Используй MOCK если:
✓ Объект вызывает внешний API (HTTP, БД, очередь)
✓ Объект медленный (БД, файловая система)
✓ Объект имеет side effects (отправка email, логирование)
✓ Объект недетерминирован (случайные числа, время)
✓ Объект сложный и трудно инициализировать
Используй РЕАЛЬНЫЙ объект если:
✓ Объект простой и быстрый
✓ Это core бизнес-логика (хочешь тестировать реально)
✓ Нет внешних зависимостей
✓ Инициализация простая
Best Practices
@SpringBootTest
public class BestPracticesTest {
@MockBean // Для Spring контекста
private PaymentService paymentService;
@InjectMocks // Автоматическое внедрение мокированных зависимостей
private OrderService orderService;
@Test
void testWithBestPractices() {
// 1. Конкретная подготовка (Arrange)
when(paymentService.charge(BigDecimal.valueOf(100)))
.thenReturn(true);
// 2. Одно действие (Act)
Order order = orderService.createOrder(new Order(BigDecimal.valueOf(100)));
// 3. Одна проверка (Assert)
assertThat(order.getStatus()).isEqualTo(OrderStatus.CONFIRMED);
// 4. Проверка взаимодействия
verify(paymentService, atLeastOnce()).charge(any());
}
}
Выводы
Mock объекты замещают:
- External API (HTTP сервисы, платежные системы)
- Databases (Repository, DAO)
- Message Brokers (Kafka, RabbitMQ)
- File Systems (File I/O операции)
- Email Services (отправка письма)
- Logger (для проверки логирования)
- Expensive Operations (сложные вычисления)
Правило: мокируй внешние зависимости, тестируй реально core логику!