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

Before/After аннотации в JUnit — руководство

5 min read Тестирование Обновлено 30 Mar 2026
Before/After аннотации в JUnit — руководство
Before/After аннотации в JUnit — руководство

Ноутбук с отображением Java-класса

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

О чём этот материал

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

Важно: термины

  • Аннотация — метка в коде, которую тестовый фреймворк обрабатывает перед исполнением метода.
  • Test instance — экземпляр класса теста, который создаёт фреймворк для выполнения тестов.

Зачем нужны before/after-аннотации

Тесты часто требуют подготовки и очистки: открытие соединений, подготовка файлов, очистка временных данных, сброс состояния стаба. Помещать такую логику внутрь тестов неудобно и приводит к дублированию. Аннотации before/after позволяют централизовать подготовку и освобождение ресурсов.

Короткий список типичных задач:

  • подключение и отключение к базе данных;
  • создание/удаление временных файлов;
  • инициализация общих тестовых данных;
  • логирование и сбор метрик;
  • сброс состояния одиночных сервисов/ста́бов.

Правила и обязательные требования

  • @BeforeAll и @AfterAll в JUnit Jupiter (JUnit 5) по умолчанию должны быть static.
  • Методы с этими аннотациями должны возвращать void и быть доступными (не private).
  • @BeforeEach и @AfterEach — нестатические методы, выполняются для каждого экземпляра тестового класса.
  • Последовательность выполнения: @BeforeAll → (@BeforeEach → @Test → @AfterEach)* → @AfterAll.

Пример: класс 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

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("Test class demonstrating how to use the before and after annotations.")  
class CalculatorTest {  
      @BeforeAll  
      public static void powerOnCalculator() {  
              System.out.println("The calculator is on");  
      }  
  
      @Test  
      @DisplayName("Testing method that adds two integer values.")  
      public void testAdd() {  
              assertEquals(7, Calculator.add(3, 4));  
      }       
  
      @Test  
      @DisplayName("Testing method that subtracts one integer value from another.")  
      public void testSubtract() {  
              assertEquals(6, Calculator.subtract(9, 3));  
      }  
  
      @Test  
      @DisplayName("Testing method that multiplies two integer values")  
      public void testMultiply() {  
              assertEquals(10, Calculator.multiply(5, 2));  
      }  
  
      @Test  
      @DisplayName("Testing method that divides one integer value by another")  
      public void testDivide() {  
              assertEquals(2, Calculator.divide(4, 2));  
      }  
}

Если powerOnCalculator() содержит ошибку — все тесты пометятся как не выполненные/проваленные, потому что подготовка не удалась.

Отчёт выполнения аннотации @BeforeAll в JUnit

@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;  
  
@DisplayName("Test class demonstrating how to use the before and after annotations.")  
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");  
      }  

      // ... тестовые методы как выше ...
}

Вывод аннотации @BeforeEach

Обратите внимание: @BeforeEach не static, выполняется столько раз, сколько тестовых методов.

@AfterAll — очистка после всех тестов

@AfterAll запускается после завершения всех тестов в классе. Используется для закрытия соединений, удаления файлов и других финализаций.

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

Вывод аннотации @AfterAll

@AfterEach — очистка после каждого теста

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

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

Вывод аннотации @AfterEach

Очередность в выводе: сначала @BeforeEach, затем тест, затем @AfterEach.

Частые ошибки и как их избежать

  • Сделать @BeforeAll нестатическим без дополнительных настроек. Решение: либо сделать метод static, либо использовать @TestInstance(Lifecycle.PER_CLASS).
  • Пытаться выполнять долгие операции в @BeforeEach без надобности — это замедлит весь набор тестов. Переносите тяжёлую инициализацию в @BeforeAll, если состояние может быть общим.
  • Прятать исключения в @BeforeAll/@BeforeEach — лучше позволять тестовому фреймворку показать причину ошибки.
  • Использовать общие статические поля без надлежащего сброса между тестами — это приводит к нежелательной зависимости тестов друг от друга.

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

  • @TestInstance(Lifecycle.PER_CLASS)

Если вы не хотите делать @BeforeAll static, можно настроить JUnit создавать один экземпляр тестового класса для всех тестов:

import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;

@TestInstance(Lifecycle.PER_CLASS)
class CalculatorTest {
    @BeforeAll
    void powerOnCalculator() {
        // не static
    }
    // ...
}
  • Использовать фикстуры уровня Suite или глобальные расширения (Extension) в JUnit для подготовки контекста один раз для нескольких классов тестов.

  • Внешняя инфраструктура: контейнерные тесты (Testcontainers) для изоляции БД/зависимостей.

Когда аннотации не подходят

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

Совместимость и переход с JUnit 4 на JUnit 5

В JUnit 4 эквивалентами выступали @BeforeClass, @Before, @AfterClass, @After. JUnit 5 (Jupiter) ввёл те же концепции, но с такими отличиями, как возможность менять поведение через @TestInstance и расширять поведение через Extension API.

Мини-методология для написания «правильного» набора тестов

  1. Определите ресурсы, которые можно разделять между тестами (БД, конфигурация) и которые требуют пер-теста очистки.
  2. Перенесите тяжёлую общую инициализацию в @BeforeAll; легкую и быструю — в @BeforeEach.
  3. Всегда освобождайте ресурсы в @AfterEach/@AfterAll соответственно.
  4. Не скрывайте исключения — пусть ТСР покажет причину отказа.
  5. Добавьте логирование в before/after, чтобы было видно, что именно и когда выполняется.

Чек-лист для разработчика и тестировщика

  • @BeforeAll: static или используется @TestInstance(PER_CLASS).
  • @BeforeEach: нет долгих блокирующих операций.
  • @AfterEach: все временные данные удаляются.
  • @AfterAll: корректно закрываются соединения и файлы.
  • Нет общего mutable состояния между тестами без сброса.
  • Лог сообщений в before/after понятен и полезен.

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

  • Все тесты проходят локально и в CI в изолированном окружении.
  • Подготовка и очистка выполняется корректно для каждого теста.
  • Нет утечек ресурсов (сокетов, файлов, соединений).
  • Тесты запускаются параллельно, если это требуется, и сохраняют корректность.

Краткий словарь

  • @BeforeAll: метод, выполняемый один раз перед всеми тестами класса.
  • @BeforeEach: метод, выполняемый перед каждым тестовым методом.
  • @AfterEach: метод, выполняемый после каждого тестового метода.
  • @AfterAll: метод, выполняемый один раз после всех тестов класса.

Примеры перехода и шаблоны

Шаблон для классов с дорогой инициализацией:

@TestInstance(Lifecycle.PER_CLASS)
class ExpensiveSetupTest {
    @BeforeAll
    void globalSetup() {
        // создание контейнера или долгой инфраструктуры
    }

    @BeforeEach
    void setupPerTest() {
        // подготовка, быстрые операции
    }

    @AfterEach
    void teardownPerTest() {
        // чистка после теста
    }

    @AfterAll
    void globalTeardown() {
        // остановка контейнеров, удаление временных артефактов
    }
}

Заключение

Аннотации @BeforeAll, @BeforeEach, @AfterEach и @AfterAll упрощают поддержку и читаемость тестов. Они помогают централизовать подготовку и очистку, уменьшают дублирование кода и повышают надёжность набора тестов. Выбирайте static или per-class поведение осознанно, документируйте намерения и добавляйте простую проверку в CI, чтобы избежать скрытых зависимостей между тестами.

Ключевые рекомендации:

  • Используйте @BeforeAll для дорогой общей инициализации.
  • Используйте @BeforeEach для гарантий чистоты состояния между тестами.
  • Освобождайте ресурсы в @AfterEach / @AfterAll.
  • Рассмотрите @TestInstance(Lifecycle.PER_CLASS), если не хотите static методы.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

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

Конвертация MP4 в MP3 — способы и советы
Мультимедиа

Конвертация MP4 в MP3 — способы и советы

Как настроить сетевой домен — полное руководство
Инфраструктура

Как настроить сетевой домен — полное руководство

Как добавить рамку к фото — инструменты и методы
Фото

Как добавить рамку к фото — инструменты и методы

Как подключить Fitbit к iPhone — полное руководство
Гаджеты

Как подключить Fitbit к iPhone — полное руководство

Проверка совместимости игр Steam с Steam Deck
Игры

Проверка совместимости игр Steam с Steam Deck

Групповые письма с iPhone и iPad
iOS

Групповые письма с iPhone и iPad