JUnit 5: организация структуры тестовых пакетов (unit, integration, e2e)
Организация тестов в проекте: unit, integration, e2e
Кратко (слайд):
Тесты разделяются по уровням:
• Unit — проверяют отдельные методы или классы (изолированно, без Spring).
• Integration — проверяют взаимодействие нескольких компонентов (с базой, контекстом Spring).
• E2E (End-to-End) — проверяют работу всей системы через реальные API или UI.
Структура проекта:
src/
├─ main/java/... → код приложения
└─ test/java/... → тесты
├─ unit/ → модульные
├─ integration/ → интеграционные
└─ e2e/ → сквозные
Каждый уровень использует свои инструменты:
Unit → JUnit + Mockito
Integration → @SpringBootTest, Testcontainers
E2E → RestAssured, Selenium, Postman/Newman
Подробное объяснение
В профессиональных проектах тестирование организовано по уровням. Это помогает контролировать качество кода на всех этапах и не путать тесты разного назначения.
1. Unit-тесты (модульные)
Это основа.
Они проверяют один класс или метод в изоляции — без базы, без Spring, без внешних сервисов.
Цель: доказать, что внутренняя логика класса работает корректно.
Пример: тестируем DiscountService, подменяя зависимости на моки.
class DiscountServiceTest {
@Mock
private ProductRepository repository;
@InjectMocks
private DiscountService service;
@Test
void shouldApplyDiscount() {
Product product = new Product("Laptop", 1000);
Mockito.when(repository.findPrice("Laptop")).thenReturn(1000.0);
double price = service.calculatePrice("Laptop");
Assertions.assertEquals(900.0, price); // скидка 10%
}
}
Юнит-тесты не обращаются к Spring.
Они создают объект через new или с помощью @InjectMocks и работают мгновенно — за миллисекунды.
Таких тестов в проекте обычно 70–80%.
2. Integration-тесты (интеграционные)
Проверяют взаимодействие между компонентами.
Это может быть связка «сервис + репозиторий + база данных» или «контроллер + сервис».
В Spring Boot интеграционные тесты обычно используют @SpringBootTest.
Spring поднимает контекст, создаёт бины и подключает тестовую БД (например, H2 или контейнер с PostgreSQL).
@SpringBootTest
@Transactional
class UserRepositoryTest {
@Autowired
private UserRepository repository;
@Test
void shouldSaveAndFindUser() {
User user = new User("Alice");
repository.save(user);
Optional<User> found = repository.findById(user.getId());
Assertions.assertTrue(found.isPresent());
}
}
Spring автоматически откатывает транзакции после каждого теста,
поэтому база остаётся чистой.
Иногда такие тесты используют Testcontainers — чтобы запускать реальную БД в Docker,
не требуя установки PostgreSQL или MySQL на машине разработчика.
3. End-to-End (E2E, сквозные тесты)
Эти тесты имитируют настоящего пользователя.
Проверяется всё приложение целиком — от REST API до базы данных.
E2E-тесты используют:
RestAssured — для HTTP-запросов к реальному серверу,
Selenium / Selenide / Playwright — для UI-тестирования,
Postman / Newman — для API-проверок без кода.
Пример API-теста с RestAssured:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserApiE2ETest {
@LocalServerPort
private int port;
@Test
void shouldReturnUserList() {
RestAssured.given()
.baseUri("http://localhost:" + port)
.when()
.get("/api/users")
.then()
.statusCode(200)
.body("[0].username", Matchers.notNullValue());
}
}
E2E-тесты медленнее, зато проверяют весь путь: контроллер → сервис → репозиторий → база.
4. Организация структуры каталогов
Рекомендуемая структура в src/test/java:
src/test/java/com/example/
│
├── unit/
│ ├── service/
│ └── utils/
│
├── integration/
│ ├── repository/
│ └── service/
│
└── e2e/
├── api/
└── ui/
Это помогает явно видеть, какие тесты что проверяют.
CI/CD пайплайн может запускать их по уровням:
mvn test -Dgroups=unit
mvn verify -Dgroups=integration
mvn verify -Dgroups=e2e
5. Сочетание уровней
Unit-тесты — быстрые, дают мгновенную обратную связь.
Integration-тесты — проверяют реальную связность.
E2E — завершающий контроль перед релизом.
Их можно представить как пирамиду тестирования:
E2E (10%)
Integration (20%)
Unit tests (70%)
Чем выше слой — тем дороже тест, тем реже его нужно запускать.
Поэтому CI-системы обычно прогоняют unit при каждом коммите,
а интеграционные и e2e — по расписанию или при сборке релиза.
Итог:
JUnit и Spring Boot позволяют выстроить тестовую архитектуру, где каждый слой отвечает за свой уровень надёжности.
При такой организации команда всегда видит, где ошибка — в логике, в интеграции или в полном цикле работы приложения.
