Гид по технологиям

Использование @BeforeAll, @BeforeEach, @AfterAll и @AfterEach в JUnit

6 min read Тестирование Обновлено 28 Dec 2025
JUnit: @BeforeAll, @BeforeEach, @AfterAll, @AfterEach
JUnit: @BeforeAll, @BeforeEach, @AfterAll, @AfterEach

JUnit предоставляет четыре ключевые аннотации для управления жизненным циклом тестов: @BeforeAll и @AfterAll выполняются один раз на класс (обычно статические методы), а @BeforeEach и @AfterEach — перед и после каждого теста соответственно. Эти фикстуры помогают настраивать окружение (подключение к БД, подготовка файлов) и освобождать ресурсы; есть нюансы со статикой, параллельным запуском и TestInstance.

Ноутбук, отображающий Java-класс калькулятора

JUnit часто применяется для модульного тестирования Java-приложений. В дополнение к самих тестам часто нужны вспомогательные действия: создание соединений, подготовка ресурсов, очистка после теста. Для этого JUnit предоставляет пару «до» и «после» аннотаций, которые позволяют централизованно организовать подготовку и освобождение ресурсов.

Что делает каждая аннотация

  • @BeforeAll — выполнить один раз до запуска всех тестов класса. Подходит для дорогих операций: подключение к БД, инициализация общей структуры.
  • @AfterAll — выполнить один раз после завершения всех тестов класса. Подходит для закрытия соединений и очистки глобальных ресурсов.
  • @BeforeEach — выполнить перед каждым тестовым методом. Используется для подготовки чистого окружения для каждого теста.
  • @AfterEach — выполнить после каждого тестового метода. Используется для отката изменений и освобождения локальных ресурсов.

Каждая из аннотаций имеет обязательные требования к сигнатуре методов в стандартном поведении JUnit Jupiter (по умолчанию):

  • @BeforeAll и @AfterAll: метод должен быть public static void (если не используется @TestInstance).
  • @BeforeEach и @AfterEach: метод должен быть public void и не static.

Ниже — подробные примеры на простом классе калькулятора.

Пример: Calculator

Класс калькулятора с базовыми арифметическими операциями:

package com.app;

public class Calculator {
    public static int add(int num1, int num2) {
        return num1 + num2;
    }
    public static int subtract(int num1, int num2) {
        return num1 - num2;
    }
    public static int multiply(int num1, int num2) {
        return num1 * num2;
    }
    public static int divide(int num1, int num2) {
        return num1 / num2;
    }
}

Пример использования @BeforeAll

Метод с аннотацией @BeforeAll выполняется один раз перед всеми тестами. В классическом варианте он должен быть static.

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;

@DisplayName("Класс тестов, демонстрирующий before/after аннотации")
class CalculatorTest {

    @BeforeAll
    public static void powerOnCalculator() {
        System.out.println("The calculator is on");
    }

    @Test
    @DisplayName("Тест сложения двух целых чисел")
    public void testAdd() {
        assertEquals(7, Calculator.add(3, 4));
    }

    @Test
    @DisplayName("Тест вычитания одного целого из другого")
    public void testSubtract() {
        assertEquals(6, Calculator.subtract(9, 3));
    }

    @Test
    @DisplayName("Тест умножения двух целых чисел")
    public void testMultiply() {
        assertEquals(10, Calculator.multiply(5, 2));
    }

    @Test
    @DisplayName("Тест деления одного целого на другой")
    public void testDivide() {
        assertEquals(2, Calculator.divide(4, 2));
    }
}

В данном примере powerOnCalculator() печатает строку один раз перед запуском всех тестов.

Отчёт выполнения с @BeforeAll

Важно: метод, помеченный @BeforeAll, сам по себе не отображается как тест в отчёте. Если в нём возникнет исключение, оно прервет выполнение тестов и пометит запуск как провал.

Пример использования @BeforeEach

@BeforeEach выполняется перед каждым тестовым методом. Если в классе четыре теста, @BeforeEach выполнится четыре раза.

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;

@DisplayName("Класс тестов, демонстрирующий before/after аннотации")
class CalculatorTest {

    @BeforeAll
    public static void powerOnCalculator() {
        System.out.println("The calculator is on");
    }

    @BeforeEach
    public void clearCalculator() {
        System.out.println("The calculator is ready");
    }

    @Test
    @DisplayName("Тест сложения двух целых чисел")
    public void testAdd() {
        assertEquals(7, Calculator.add(3, 4));
    }

    @Test
    @DisplayName("Тест вычитания одного целого из другого")
    public void testSubtract() {
        assertEquals(6, Calculator.subtract(9, 3));
    }

    @Test
    @DisplayName("Тест умножения двух целых чисел")
    public void testMultiply() {
        assertEquals(10, Calculator.multiply(5, 2));
    }

    @Test
    @DisplayName("Тест деления одного целого на другой")
    public void testDivide() {
        assertEquals(2, Calculator.divide(4, 2));
    }
}

Вывод покажет строку “The calculator is ready” перед каждым тестом.

Вывод для @BeforeEach

Обратите внимание: @BeforeEach не статичен и выполняется после @BeforeAll.

Пример использования @AfterAll

@AfterAll выполняется один раз в конце выполнения всех тестов класса. Обычно используется для освобождения глобальных ресурсов.

import org.junit.jupiter.api.AfterAll;

@AfterAll
public static void powerOffCalculator() {
    System.out.println("The calculator is off");
}

Размещение такого метода в тестовом классе приведёт к выводу сообщения в конце всех запусков.

Вывод для @AfterAll

Пример использования @AfterEach

@AfterEach выполняется после каждого тестового метода, что удобно для очистки состояния, удаления временных файлов или отката изменений.

import org.junit.jupiter.api.AfterEach;

@AfterEach
public void returnResults() {
    System.out.println("The results are ready");
}

При четырёх тестах метод с @AfterEach выполнится четыре раза — по одному разу после каждого теста.

Вывод для @AfterEach

Важные замечания и частые ошибки

  • Статические требования. По умолчанию @BeforeAll и @AfterAll должны быть static. Если вам нужно не static поведение, используйте @TestInstance(TestInstance.Lifecycle.PER_CLASS), тогда методы могут быть нестатичными.
  • Параллельный запуск тестов. При параллельном выполнении тестовых методов глобальные ресурсы, установленные в @BeforeAll, должны быть потокобезопасны. Иначе возможны гонки и флаки.
  • Исключения в фикстурах. Исключение в @BeforeAll или @BeforeEach обычно прерывает соответствующие тесты. Обрабатывайте исключения внутри фикстур, если хотите продолжить выполнение.
  • Порядок выполнения. @BeforeAll → @BeforeEach → тест → @AfterEach → @AfterAll.
  • Взаимодействие с менеджерами ресурсов. Для интеграционных тестов лучше использовать тестовые контейнеры и managed resources (например, Testcontainers), а не ручные connect/disconnect в фикстурах.

Important: если вам нужно подготовить один и тот же изменяемый объект для каждого теста, создавайте его в @BeforeEach, чтобы тесты оставались независимыми.

Альтернативные подходы

  • @TestInstance(TestInstance.Lifecycle.PER_CLASS) — позволяет использовать нестатические @BeforeAll/@AfterAll и хранить состояние класса между тестами.
  • JUnit Extensions — реализуйте Extension для повторно используемой логики подготовки/очистки (например, для установки окружения, временных файлов, мок-серверов).
  • Testcontainers и другие фреймворки — для управления временными БД и сервисами вместо ручной инициализации.

Пошаговая мини-методология подготовки фикстур

  1. Определите уровень ресурса: глобальный для класса или локальный для теста.
  2. Если ресурс общий и дорогой — используйте @BeforeAll/@AfterAll.
  3. Если ресурс должен быть «чистым» для каждого теста — используйте @BeforeEach/@AfterEach.
  4. Обработайте исключения и сделайте операции идемпотентными, чтобы пропуск/повторение не ломали тестовую среду.
  5. Добавьте логирование и, при необходимости, артефакты для диагностики провалов.

Ментальные модели и эвристики

  • «Подготовка — Тест — Очистка»: каждая проверка должна иметь атомарное начало и конец.
  • «Дорогие вещи один раз, дешёвые — каждый тест»: подключение к БД один раз, очистка таблиц перед каждым тестом.
  • «Тесты должны быть изолированы»: избегайте общих изменяемых состоятелй между тестами.

Когда это может не сработать

  • Если тесты запускаются параллельно и фикстуры не потокобезопасны — возможны непредсказуемые результаты.
  • Когда фикстура выполняет тяжёлые операции и делает тесты медленными — возможно лучше запускать отдельный набор интеграционных тестов.
  • Если настройка требует контекста, которого нет в unit-тестах (например, контейнеры ОС), лучше использовать интеграционные тесты.

Рекомендации по безопасности и ресурсам

  • Всегда закрывайте внешние соединения в @AfterAll или в try-with-resources внутри фикстуры.
  • Ограничьте влияние глобальных статических состояний — используйте их только если это оправдано.

Контроль качества: чек-листы по ролям

Разработчик:

  • Убедиться, что @BeforeEach и @AfterEach делают тесты детерминированными.
  • Не хранить состояние между тестами без явной причины.
  • Логировать ошибки в фикстурах для последующего анализа.

QA-инженер:

  • Проверить, что исключения в @BeforeAll помечают всю сессию как провал.
  • Запустить тесты в режиме параллельного выполнения и проверить на гонки.

DevOps/релизный инженер:

  • Оценить время выполнения фикстур и их влияние на CI.
  • Рассмотреть разделение unit- и integration-тестов в разные пайплайны.

Критерии приёмки

  • Тесты выполняются без зависимостей друг от друга.
  • @BeforeAll и @AfterAll корректно инициализируют и освобождают общие ресурсы.
  • При параллельном выполнении тестов отсутствуют гонки на общих ресурсах.

Короткий глоссарий

  • JUnit — фреймворк для модульного тестирования Java.
  • Тестовый метод — метод с аннотацией @Test, который выполняет проверку.
  • Фикстура — подготовка и очистка окружения теста (методы с before/after аннотациями).
  • @TestInstance — аннотация, управляющая временем жизни экземпляра тестового класса.

Дерево принятия решения (Mermaid)

flowchart TD
  A[Нужно настроить ресурс для тестов?] -->|Да| B{Ресурс общий для всего набора тестов?}
  A -->|Нет| Z[Ничего не делать]
  B -->|Да| C{Нужен один экземпляр на класс и позволяет static?}
  B -->|Нет| D[Использовать @BeforeEach/@AfterEach]
  C -->|Да| E[Использовать @BeforeAll/@AfterAll 'static']
  C -->|Нет| F[Использовать @TestInstance'PER_CLASS' и нестатические @BeforeAll/@AfterAll]

Примеры отказа и обходные пути

  • Проблема: @BeforeAll static не подходит, потому что вы хотите доступ к нестатическим полям. Решение: добавить @TestInstance(TestInstance.Lifecycle.PER_CLASS) к тестовому классу.

  • Проблема: тесты флаки при параллельном запуске. Решение: сделать глобальные ресурсы потокобезопасными или перейти к пер-тестовым ресурсам.

Лучшие практики

  • Минимизируйте работу в @BeforeAll до действительно необходимого.
  • Используйте @AfterEach для гарантированного отката побочных эффектов.
  • Логируйте начало и конец фикстур для быстрой диагностики.
  • По возможности избегайте глобального изменяемого состояния.

Краткое резюме

JUnit-аннотации @BeforeAll, @BeforeEach, @AfterAll и @AfterEach помогают структурировать подготовку и очистку окружения для тестов. Выбор между ними зависит от того, насколько ресурс общ и дорог в инициализации, а также от требований к изоляции и потокобезопасности. При необходимости используйте @TestInstance или расширения JUnit для более гибкой настройки.

Summary:

  • Применяйте @BeforeAll/@AfterAll для глобальной подготовки и очистки.
  • Применяйте @BeforeEach/@AfterEach для локальной подготовки и отката.
  • Контролируйте побочные эффекты и учитывайте параллельный запуск.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Учите язык с Toucan, не теряя продуктивности
Изучение языков

Учите язык с Toucan, не теряя продуктивности

Как попросить прибавку: 6 советов и шаблоны
Карьера

Как попросить прибавку: 6 советов и шаблоны

Буфер обмена подменяет адрес кошелька — удаление вредоноса
Безопасность

Буфер обмена подменяет адрес кошелька — удаление вредоноса

Заклинания Гарри Поттера в iPhone
iPhone

Заклинания Гарри Поттера в iPhone

Решение проблем Netflix быстро
Streaming

Решение проблем Netflix быстро

Как пользоваться Snapchat: полный гид
Социальные сети

Как пользоваться Snapchat: полный гид