Тестирование в Java: unit, integration, e2e — полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Тестирование в Java: от модульных до сквозных тестов

Качественное тестирование — основа надежного программного обеспечения. В мире Java существует устоявшаяся пирамида тестирования, которая включает три основных уровня: unit (модульные), integration (интеграционные) и e2e (сквозные) тесты. В этой статье мы разберем каждый из них, приведем практические примеры на Java и расскажем, как правильно выстроить стратегию тестирования в вашем проекте.

1. Unit-тестирование: основа пирамиды

Unit-тесты проверяют отдельные компоненты программы в изоляции — как правило, один класс или метод. Они выполняются быстро, не требуют внешних зависимостей (БД, API) и должны покрывать большинство бизнес-логики.

Инструменты для unit-тестов в Java

  • JUnit 5 — стандартный фреймворк для написания тестов.
  • Mockito — библиотека для создания мок-объектов.
  • AssertJ — fluent-ассерты для читаемых проверок.

Пример unit-теста с JUnit 5 и Mockito

Допустим, у нас есть сервис OrderService, который зависит от PaymentGateway. Мы хотим протестировать логику расчета скидки, не вызывая реальный платежный шлюз.

// Класс для тестирования
public class OrderService {
    private final PaymentGateway paymentGateway;

    public OrderService(PaymentGateway paymentGateway) {
        this.paymentGateway = paymentGateway;
    }

    public double calculateDiscount(double amount) {
        if (amount > 1000) {
            return amount * 0.1; // 10% скидка
        }
        return 0;
    }
}

// Unit-тест
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;

@ExtendWith(MockitoExtension.class)
public class OrderServiceTest {

    @Mock
    private PaymentGateway paymentGateway;

    @InjectMocks
    private OrderService orderService;

    @Test
    public void testCalculateDiscount_AmountAbove1000_Returns10Percent() {
        double result = orderService.calculateDiscount(1500);
        assertEquals(150.0, result, 0.001);
    }

    @Test
    public void testCalculateDiscount_AmountBelow1000_ReturnsZero() {
        double result = orderService.calculateDiscount(500);
        assertEquals(0.0, result);
    }
}

Лучшие практики: используйте @BeforeEach для инициализации общих данных, избегайте тестирования приватных методов напрямую, следуйте правилу "одна проверка на один тест".

2. Интеграционное тестирование: проверка взаимодействия

Интеграционные тесты проверяют, как несколько модулей работают вместе — например, взаимодействие сервиса с базой данных, очередями сообщений или внешними API. В отличие от unit-тестов, здесь используются реальные или in-memory зависимости.

Инструменты для интеграционных тестов

  • Spring Boot Test — для тестирования Spring-приложений.
  • Testcontainers — для поднятия Docker-контейнеров (БД, Kafka).
  • H2 Database — in-memory база для быстрых тестов.

Пример интеграционного теста с Spring Boot

Тестируем REST-контроллер с реальным подключением к БД (в тесте используем H2).

// Контроллер
@RestController
@RequestMapping("/api/users")
public class UserController {
    private final UserRepository userRepository;

    public UserController(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("User not found"));
    }
}

// Интеграционный тест
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void testGetUser_ExistingUser_ReturnsUserData() {
        // Предполагаем, что пользователь с ID=1 существует (через data.sql)
        ResponseEntity<User> response = restTemplate.getForEntity("/api/users/1", User.class);
        assertThat(response.getStatusCode().is2xxSuccessful()).isTrue();
        assertThat(response.getBody().getName()).isEqualTo("John Doe");
    }
}

Важно: используйте @Transactional для отката изменений после каждого теста, чтобы не загрязнять базу данных.

3. E2E-тестирование: проверка с точки зрения пользователя

Сквозные (end-to-end) тесты имитируют поведение реального пользователя: открывают браузер, заполняют формы, нажимают кнопки. Они самые медленные, но дают максимальную уверенность в работоспособности системы в целом.

Инструменты для E2E-тестов

  • Selenium WebDriver — классический инструмент для автоматизации браузера.
  • Playwright для Java — современная альтернатива с лучшей поддержкой.
  • Cypress (чаще для JS, но можно использовать через API).

Пример E2E-теста с Selenium

Тестируем форму логина на веб-приложении.

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.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class LoginE2ETest {

    private WebDriver driver;

    @BeforeEach
    public void setUp() {
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--headless"); // без GUI
        driver = new ChromeDriver(options);
        driver.get("https://example.com/login");
    }

    @Test
    public void testLogin_ValidCredentials_RedirectsToDashboard() {
        driver.findElement(By.id("username")).sendKeys("testuser");
        driver.findElement(By.id("password")).sendKeys("password123");
        driver.findElement(By.id("login-btn")).click();

        String currentUrl = driver.getCurrentUrl();
        assertEquals("https://example.com/dashboard", currentUrl);
    }

    @AfterEach
    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }
}

Совет:

Блоги

Рекомендаций по книгам