Ответ
Модульные тесты изолируют и проверяют отдельные компоненты (обычно классы). Стандартный стек: JUnit 5 (тестовый фреймворк) + Mockito (библиотека для мокинг-объектов).
1. Базовая структура теста
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(MockitoExtension.class) // Интеграция JUnit с Mockito
class UserServiceTest {
@Mock
private UserRepository userRepository; // Зависимость, которую мы мокаем
@InjectMocks
private UserService userService; // Тестируемый сервис, куда внедряются моки
@Test
void getUserById_WhenUserExists_ReturnsUser() {
// 1. Arrange (Подготовка): задаем поведение мок-объектов
Long userId = 1L;
User expectedUser = new User(userId, "testUser");
when(userRepository.findById(userId)).thenReturn(Optional.of(expectedUser));
// 2. Act (Действие): вызываем тестируемый метод
User actualUser = userService.getUserById(userId);
// 3. Assert (Проверка): проверяем результат
assertNotNull(actualUser);
assertEquals(expectedUser.getId(), actualUser.getId());
assertEquals(expectedUser.getUsername(), actualUser.getUsername());
// Проверяем, что метод репозитория был вызван ровно один раз с нужным аргументом
verify(userRepository, times(1)).findById(userId);
}
@Test
void getUserById_WhenUserNotFound_ThrowsException() {
Long userId = 999L;
when(userRepository.findById(userId)).thenReturn(Optional.empty());
assertThrows(EntityNotFoundException.class, () -> {
userService.getUserById(userId);
});
}
}
2. Ключевые практики:
- Изоляция: Тест не должен зависеть от внешних сервисов (БД, API). Используйте моки.
- Читаемость: Следуйте паттерну Arrange-Act-Assert. Используйте понятные имена методов (
getUserById_WhenUserNotFound_ThrowsException). - Детерминированность: Тест всегда должен давать одинаковый результат.
- Покрытие: Пишите тесты для основных (happy path) и граничных (error cases) сценариев.
- Использование
@BeforeEach: Для инициализации общих данных перед каждым тестом.
3. Дополнительные инструменты:
@SpringBootTest: Для интеграционных тестов, поднимающих контекст Spring.TestRestTemplate/WebTestClient: Для тестирования REST контроллеров.@DataJpaTest: Для тестирования JPA-репозиториев с embedded БД.