CI/CD5 мин чтения2026-03-06

Автоматизация тестирования на Java: от курсов до продакшена

Лет пять назад я собеседовал кандидата после курсов. Резюме красивое: "яндекс практикум автоматизация тестирования на java", сертификат, проекты в портфолио. Сп

Лет пять назад я собеседовал кандидата после курсов. Резюме красивое: "яндекс практикум автоматизация тестирования на java", сертификат, проекты в портфолио. Спрашиваю: как вы запускаете тесты в CI? Тишина. А как тесты параллелить? Ещё тише.

Парень знал Selenium. Умел писать тесты. Но между "написать тест" и "тесты работают в продакшене" — пропасть. Вот про эту пропасть и поговорим. Потому что курсов куча, а вот как собрать это в работающую систему — информации мало.

Стек: что реально используется в индустрии

Если вы учитесь — скорее всего, вам дают JUnit, Selenium, может быть TestNG. Это база. Но в продакшене картинка другая.

JUnit 5 — стандарт де-факто. Если видите проект на JUnit 4 — это легаси, и его переписывают. JUnit 5 даёт параметризацию из коробки, нормальную модель расширений, наконец-то адекватные ассерты.

Selenide вместо чистого Selenium. Честно, я давно не видел проектов, где пишут на чистом Selenium. Selenide оборачивает его в человеческий API:

open("/login");
$(By.id("email")).setValue("user@test.com");
$(By.id("password")).setValue("password123");
$("button[type=submit]").click();
$(".welcome-message").shouldHave(text("Добро пожаловать"));

Не нужно думать про waits, не нужно ловить StaleElementReferenceException. Работает.

RestAssured для API-тестов. Если вы тестируете только UI — вы тестируете только UI. А бизнес-логика живёт в API. RestAssured даёт читаемые тесты:

given()
    .header("Authorization", token)
    .body(requestBody)
.when()
    .post("/api/orders")
.then()
    .statusCode(201)
    .body("id", notNullValue())
    .body("status", equalTo("CREATED"));

Да, в курсах типа "яндекс практикум автоматизация тестирования на java" или "яндекс практикум автоматизация тестирования python" это дают. Но часто — поверхностно. Фокус на написании тестов, а не на архитектуре тестового проекта.

CI/CD: где живут тесты

Тесты без CI — это просто файлы на чьём-то ноутбуке. Они не работают.

Базовая схема: код → коммит → автоматический запуск тестов → деплой. Звучит просто. Но есть нюансы.

Уровни тестирования в пайплайне

Юнит-тесты запускаются на каждый коммит. Быстро, параллельно. Если падают — билд красный, мердж запрещён.

Интеграционные тесты — на merge request. Дольше, но ещё терпимо. Используют тестовые среды, моки внешних сервисов.

E2E тесты — по расписанию или перед релизом. Медленные, хрупкие, но проверяют реальный user flow. Их не запустишь на каждый коммит — часами будут идти.

Пример GitLab CI для Java-проекта

stages:
  - build
  - test
  - e2e
  - deploy

variables:
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"

cache:
  paths:
    - .m2/repository/

build:
  stage: build
  image: maven:3.8-openjdk-17
  script:
    - mvn compile

unit-tests:
  stage: test
  image: maven:3.8-openjdk-17
  script:
    - mvn test -Dtest="*UnitTest"
  artifacts:
    reports:
      junit: target/surefire-reports/*.xml

integration-tests:
  stage: test
  image: maven:3.8-openjdk-17
  services:
    - postgres:14
    - redis:7
  variables:
    POSTGRES_DB: test_db
    POSTGRES_USER: test
    POSTGRES_PASSWORD: test
  script:
    - mvn verify -Dtest="*IntegrationTest"
  only:
    - merge_requests

e2e-tests:
  stage: e2e
  image: maven:3.8-openjdk-17
  services:
    - selenium/standalone-chrome:latest
  script:
    - mvn verify -Dtest="*E2ETest" -Dselenium.host=selenium
  only:
    - main
  when: manual

Это рабочая конфигурация. Не идеальная, но рабочая. Юнит-тесты гоняются всегда, интеграционные — только в MR, E2E — вручную на main.

GitHub Actions — альтернатива

name: Test Pipeline

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  unit-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: maven
      - run: mvn test

  integration-tests:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:14
        env:
          POSTGRES_DB: test
          POSTGRES_PASSWORD: test
        ports:
          - 5432:5432
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          java-version: '17'
      - run: mvn verify -Dtest="*IntegrationTest"

GitHub Actions удобнее для open-source. GitLab CI — для внутренних проектов. Но принцип один: тесты — часть пайплайна, не отдельная активность.

Code review тестов: что часто упускают

Тесты — это код. Их тоже нужно ревьюить. Но на практике — "тесты работают, ок, мерджим". И это проблема.

Типичные косяки, которые я вижу:

Хардкод данных. Логины, пароли, URL — всё захардкожено. Потом среду поменяли — тесты упали. Используйте конфигурацию:

public class Config {
    private static final String BASE_URL = System.getProperty("baseUrl", "https://test.example.com");
    private static final String TEST_USER = System.getProperty("testUser", "test@example.com");
    
    public static String getBaseUrl() {
        return BASE_URL;
    }
}

Запускаете так: mvn test -DbaseUrl=https://staging.example.com

Отсутствие изоляции. Тест меняет состояние, следующий тест падает. Каждый тест должен быть независим. Before/After аннотации — не просто так придуманы.

Проверка реализации вместо поведения. Тест знает про внутренности тестируемого кода. Меняется имплементация — падает тест, хотя поведение то же. Это хрупкие тесты, они съедают время на поддержку.

Параллелизация: когда тестов становится много

На одном проекте у нас было 2000+ тестов. Последовательно шли 40 минут. В CI это неприемлемо.

Решение — параллельный запуск. JUnit 5 поддерживает из коробки:

@Configuration
public class ParallelTestConfig {
    @ConfigurationParameter(key = "junit.jupiter.execution.parallel.enabled", value = "true")
    @ConfigurationParameter(key = "junit.jupiter.execution.parallel.mode.default", value = "concurrent")
    @ConfigurationParameter(key = "junit.jupiter.execution.parallel.config.strategy", value = "dynamic")
    @ConfigurationParameter(key = "junit.jupiter.execution.parallel.config.dynamic.factor", value = "2")
}

В Maven — Surefire plugin с настройками:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.1.2</version>
    <configuration>
        <parallel>methods</parallel>
        <threadCount>4</threadCount>
        <perCoreThreadCount>true</perCoreThreadCount>
    </configuration>
</plugin>

С 40 минут до 12. Уже лучше.

Но есть нюанс. Если тесты используют общие ресурсы — базу, файлы — параллелизация сломается. Нужна изоляция. Container Testcontainers — ваш друг:

@Testcontainers
public class BaseIntegrationTest {
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:14")
        .withDatabaseName("test");
    
    @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);
    }
}

Каждый тестовый класс получает свою базу. Изоляция полная.

Отчёты и аналитика

Тесты падают. Это нормально. Ненормально — когда вы не понимаете, почему.

Allure — стандарт для отчётов. Интегрируется с JUnit, TestNG, почти со всем. Даёт историю, тренды, понятные репорты:

@Epic("User Management")
@Feature("Login")
public class LoginTest {
    
    @Story("Successful login")
    @Severity(SeverityLevel.BLOCKER)
    @Test
    public void userCanLoginWithValidCredentials() {
        // test body
    }
}

Отчёт покажет: какие фичи покрыты, где проблемы, какие тесты самые хрупкие. Хрупкие тесты — это отдельная боль. Если тест падает в 30% случаев без изменения кода — это плохой тест. Его нужно переписывать или удалять.

Flaky-тесты съедают доверие к тестированию. Команда привыкает: "упало, перезапустим". А там уже реальный баг. Но никто не смотрит.

Что в итоге

Автоматизация тестирования — это не про Selenium. Это про систему. Процесс. Инфраструктуру.

Курсы дают базу. "Яндекс практикум автоматизация тестирования на java" или аналоги — окей для старта. Но реальный опыт — это когда вы настроили CI, разобрались с параллелизацией, победили flaky-тесты, внедрили нормальные отчёты.

Ну и последнее. Code review тестов так же важен, как и code review продакшен-кода. Мы в команде используем Distiq — это AI-бот, который автоматически проверяет каждый MR. Он ловит типовые проблемы: захардкоженные креды, missing asserts, некорректную изоляцию. Не заменяет человека, но экономит время на базовые проверки. Ссылка: distiq.ru, если интересно.

Попробуйте Distiq для автоматического code review

AI-бот анализирует каждый MR/PR и оставляет комментарии с замечаниями. Интеграция за 2 минуты.

Попробовать бесплатно

Похожие статьи