Введение: Зачем нужно тестирование в Java?
Каждый Java-разработчик хотя бы раз сталкивался с ситуацией, когда после изменения одной строчки кода «ломалась» вся система. Чтобы избежать подобных катастроф, индустрия выработала стройную систему тестирования. В этой статье мы разберём три уровня тестирования в Java: unit (модульное), integration (интеграционное) и e2e (сквозное). Вы узнаете, чем они отличаются, какие инструменты использовать и как правильно писать тесты на Java.
Независимо от того, пишете ли вы микросервисы на Spring Boot или десктопные приложения, понимание пирамиды тестирования — обязательный навык. Поехали!
1. Unit-тестирование: Фундамент качества
Unit-тесты (модульные тесты) проверяют работу отдельных классов или методов в изоляции от внешних зависимостей. Это самый быстрый и дешёвый уровень тестирования.
1.1 Основные инструменты
- JUnit 5 — стандартный фреймворк для Java (последняя версия — Jupiter).
- Mockito — библиотека для создания mock-объектов (заглушек).
- AssertJ — библиотека с богатым набором fluent-ассертов.
1.2 Пример unit-теста с JUnit 5 и Mockito
Допустим, у нас есть сервис UserService, который зависит от репозитория UserRepository:
public class UserService {
private final UserRepository repository;
public UserService(UserRepository repository) {
this.repository = repository;
}
public String getUserName(Long id) {
User user = repository.findById(id);
return user != null ? user.getName() : "Unknown";
}
}
Напишем unit-тест, изолируя внешнюю зависимость через Mockito:
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.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void testGetUserName_WhenUserExists() {
// Arrange
User mockUser = new User(1L, "Alice");
when(userRepository.findById(1L)).thenReturn(mockUser);
// Act
String result = userService.getUserName(1L);
// Assert
assertEquals("Alice", result);
}
@Test
void testGetUserName_WhenUserNotFound() {
when(userRepository.findById(99L)).thenReturn(null);
String result = userService.getUserName(99L);
assertEquals("Unknown", result);
}
}
Правило хорошего тона: один тест — одна проверка. Используйте понятные имена методов (например, testGetUserName_WhenUserExists).
2. Integration-тестирование: Проверка взаимодействий
Интеграционные тесты проверяют, как компоненты системы работают вместе: база данных, файловая система, внешние API. Они медленнее unit-тестов, но дают больше уверенности в корректности связки.
2.1 Инструменты для интеграционного тестирования Java
- Spring Boot Test —
@SpringBootTestдля поднятия контекста приложения. - Testcontainers — запуск реальных БД (PostgreSQL, MySQL) в Docker-контейнерах.
- H2 — in-memory база данных для быстрых тестов.
2.2 Пример интеграционного теста с Spring Boot и Testcontainers
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest
@Testcontainers
class UserRepositoryIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
private UserRepository userRepository;
@Test
void testSaveAndFindUser() {
User user = new User(null, "Bob");
User saved = userRepository.save(user);
User found = userRepository.findById(saved.getId()).orElse(null);
assertThat(found).isNotNull();
assertThat(found.getName()).isEqualTo("Bob");
}
}
Важно: В реальных проектах не злоупотребляйте @SpringBootTest. Для тестирования только слоя доступа используйте @DataJpaTest — это значительно быстрее.
3. E2E-тестирование: Сквозная проверка пользовательского сценария
E2E (End-to-End) тесты имитируют действия реального пользователя. Они запускают приложение целиком (фронтенд + бэкенд + БД) и проверяют критические сценарии: регистрацию, оформление заказа, логин.
3.1 Инструменты для E2E тестирования Java
- Selenium WebDriver — классика для веб-приложений.
- Playwright for Java — современная альтернатива с лучшей поддержкой ожиданий.
- Cucumber — BDD-фреймворк для написания тестов на естественном языке (Gherkin).
3.2 Пример E2E теста с Selenium и JUnit
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
import static org.junit.jupiter.api.Assertions.assertTrue;
class LoginE2ETest {
private WebDriver driver;
@BeforeEach
void setUp() {
driver = new ChromeDriver();
driver.manage().window().maximize();
driver.get("https://test-site.com/login");
}
@Test
void testSuccessfulLogin() {
WebElement username