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

Внедрение зависимостей в JUnit 5

5 min read Тестирование Обновлено 28 Dec 2025
Внедрение зависимостей в JUnit 5
Внедрение зависимостей в JUnit 5

JUnit 5 поддерживает внедрение зависимостей (Dependency Injection, DI) в конструкторы и тестовые методы — это упрощает повторное использование данных и делает тесты короче и надёжнее. Используйте встроенные ParameterResolver’ы (например, TestInfo) для доступа к метаданным теста или подключайте собственные расширения через @ExtendWith.

Ноутбук с кодом и двумя руками на клавиатуре

Цель модульного тестирования — максимально быстро обнаруживать ошибки. JUnit 5 предоставляет удобный и эффективный инструмент: внедрение зависимостей (DI). Это помогает избавляться от повторного создания одних и тех же тестовых данных в разных классах и повышает читаемость тестов.

Что такое внедрение зависимостей

В одном предложении: внедрение зависимостей — это шаблон проектирования, при котором объект получает свои внешние зависимости извне, а не создаёт их сам.

Ключевая польза: вы делите ответственность — создание данных и проверка поведения отделены. Это снижает дублирование и делает тесты предсказуемыми.

Краткое определение терминов

  • Dependency Injection (DI): механизм передачи зависимостей извне.
  • ParameterResolver: интерфейс JUnit 5, который создаёт и передаёт параметры в тест.
  • TestInfo: встроенный тип, через который можно получать метаданные текущего теста.

Внедрение зависимостей в JUnit 5

JUnit 5 разрешает параметры в конструкторах тестовых классов и в самих тестовых методах. Это значит, что вам не нужно писать вспомогательные статики или фабрики только для передачи контекста теста.

Особенности:

  • Можно передавать сколько угодно параметров — главное, чтобы для каждого имелся доступный ParameterResolver.
  • JUnit поставляется с несколькими встроенными резолверами (например, TestInfo, RepetitionInfo и др.).
  • Для сторонних типов подключайте свои расширения через @ExtendWith.

Пример: использование TestInfo

Ниже пример, который демонстрирует внедрение TestInfo в конструктор и методы тестового класса. TestInfo предоставляет API для получения имени теста и другой метаинформации.

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

class InfoTestInterfaceTest {

    // Внедрение объекта TestInfo в конструктор тестового класса
    InfoTestInterfaceTest(TestInfo testInfo) {
        assertEquals("InfoTestInterfaceTest", testInfo.getDisplayName());
    }

    // Внедрение объекта TestInfo в метод
    @Test
    void testMethodName(TestInfo testInfo) {
        assertEquals("testMethodName(TestInfo)", testInfo.getDisplayName());
    }

    @Test
    @DisplayName("method using the @DisplayName annotation")
    void testMethodNameTwo(TestInfo testInfo) {
        assertEquals("method using the @DisplayName annotation", testInfo.getDisplayName());
    }
}

TestInfo имеет несколько полезных методов; наиболее часто используется getDisplayName(), который возвращает отображаемое имя текущего теста — либо сгенерированное JUnit, либо явно заданное через @DisplayName.

Отчёт JUnit о внедрении зависимостей

Использование DI в методах подготовки и уборки

Аннотации @BeforeAll, @BeforeEach, @AfterAll и @AfterEach также поддерживают параметры. Передавайте нужные зависимости в параметры этих методов вместо обращения к статическим полям или созданию ресурсов в каждом тесте.

Пример:

@BeforeEach
void setUp(TestInfo testInfo) {
    // подготовить окружение с учётом имени теста
}

@AfterEach
void tearDown(TestInfo testInfo) {
    // очистка ресурсов
}

Важно: @BeforeAll для нестатических методов требует использования расширения @TestInstance(Lifecycle.PER_CLASS) или статического метода при обычном жизненном цикле.

Когда DI в тестах не подходит или ограничен (контрпримеры)

  • Когда зависимость имеет глобальное состояние, управлять которым удобнее через специальные фикстуры или контейнеры (например, embedded DB с общим lifecycle).
  • Для параметров, которые зависят от рантайма среды (например, системные переменные, реальные API-сервис) иногда проще использовать имитации/моки и фабрики, чем резолверы.
  • Если ваш проект использует старые версии JUnit (до 5), DI в тестах недоступен.

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

  • Фабрики тестовых данных (Test Data Builders): хороший подход для сложных объектов.
  • Статические помощники (utility methods): просты, но приводят к дублированию состояния.
  • Внешние контейнеры/fixtures (Docker, Testcontainers): когда требуется интеграция с сервисами.

Памятки и эвристики

  • Если одинаковые параметры появляются в многих тестах — подключайте ParameterResolver или расширение.
  • Начинайте с TestInfo и RepetitionInfo — они встроены и не требуют дополнительной конфигурации.
  • Не делайте ParameterResolver слишком «толстым» — пусть он возвращает простые и предсказуемые объекты.
  • Избегайте побочных эффектов в резолверах.

Шпаргалка: какие параметры JUnit умеет резолвить из коробки

  • TestInfo — метаданные теста (getDisplayName(), getTestMethod() и т.д.)
  • RepetitionInfo — информация о повторяющемся тесте
  • TestReporter — для логирования дополнительных данных

Если нужно больше — реализуйте ParameterResolver или используйте @ExtendWith с существующими расширениями.

Ролевая чек-лист: кто и что должен проверить

Разработчик

  • Проверить, что тесты читаемы и не создают лишнего состояния.
  • Перевести повторяющиеся параметры в резолверы или фабрики.

Team Lead / QA

  • Убедиться, что внедрение зависимостей не скрывает важных побочных эффектов.
  • Проверить, что lifecycle (BeforeAll/AfterAll) корректен для каждого класса.

Инженер по инфраструктуре

  • Оценить необходимость использовать глобальные ресурсы (интеграционные контейнеры) вместо резолверов.

Маленькая методология внедрения DI в тестовый проект (шаги)

  1. Найдите дублирование: одинаковые данные/настройки в нескольких тестах.
  2. Выделите данные в ParameterResolver или отдельную фабрику.
  3. Покрытие: добавьте тесты для нового резолвера (проверка предсказуемости и отсутствия побочных эффектов).
  4. Документируйте и снабдите примерами использования в репозитории.
  5. Рефакторите тесты поэтапно, чтобы не ломать CI.

Пример простого собственного ParameterResolver (схема)

  • Реализуйте интерфейс ParameterResolver.
  • В методе supportsParameter возвращайте true для целевого типа.
  • В resolveParameter создавайте/возвращайте объект.
  • Зарегистрируйте резолвер через @ExtendWith(YourResolver.class).
public class MyResolver implements ParameterResolver {
    @Override
    public boolean supportsParameter(ParameterContext pc, ExtensionContext ec) {
        return pc.getParameter().getType().equals(MyType.class);
    }

    @Override
    public Object resolveParameter(ParameterContext pc, ExtensionContext ec) {
        return new MyType(/*...*/);
    }
}

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

  • Тесты используют DI для общих параметров, дублирование кода уменьшено.
  • Резолверы не имеют скрытых побочных эффектов.
  • CI выполняет тесты стабильно: нет межтестовых зависимостей.

Однострочные определения

  • ParameterResolver: механизм создания и передачи параметров в тесты.
  • TestInfo: объект с метаданными текущего теста.

Decision flowchart

flowchart TD
  A[Есть дублирование тестовых данных?] -->|Да| B{Параметры простые?}
  B -->|Да| C[Создать ParameterResolver]
  B -->|Нет| D[Использовать фабрики/Builders или Testcontainers]
  A -->|Нет| E[Оставить текущую реализацию]
  C --> F[Зарегистрировать через @ExtendWith]
  D --> F
  F --> G[Рефакторинг тестов]

Резюме

Внедрение зависимостей в JUnit 5 делает тестовый код проще, уменьшает дублирование и улучшает поддержку. Начните с встроенных резолверов (TestInfo, RepetitionInfo), затем при необходимости добавьте свои ParameterResolver’ы и используйте @ExtendWith для регистрации. Внедряйте DI итеративно, проверяя отсутствие побочных эффектов и стабильность CI.

Короткие советы

  • Используйте DI для передачи контекста и метаданных.
  • Для сложных внешних ресурсов смотрите в сторону контейнеров и фабрик.
  • Пишите тесты для собственных резолверов.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

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

Восстановить удалённые закладки в Edge
браузер

Восстановить удалённые закладки в Edge

Клонирование приложений на Samsung через Dual Messenger
Мобильные советы

Клонирование приложений на Samsung через Dual Messenger

TypeORM + NestJS: быстрая интеграция
Разработка

TypeORM + NestJS: быстрая интеграция

Отключить автоматические обновления Windows 11
Windows

Отключить автоматические обновления Windows 11

Почему не копируется текст в браузере — быстрое решение
Браузеры

Почему не копируется текст в браузере — быстрое решение

Как настроить голосовую почту на Android
Мобильные телефоны

Как настроить голосовую почту на Android