Как реализовать тестирование микросервисов, включая юнит и интеграционные тесты?
Что проверяют
Умеете ли вы разложить тесты Spring-сервиса по уровням и держать набор быстрым. Следом обычно спрашивают, чем @SpringBootTest отличается от срезов вроде @WebMvcTest и как тестировать работу с базой без H2.
Как отвечать
Раскладка по уровням:
- юнит-тесты: JUnit 5, Mockito с
@ExtendWith(MockitoExtension.class),@Mockи@InjectMocks, проверки через AssertJ. Контекст Spring не поднимается, тесты идут миллисекунды. Проще всего тестируются сервисы с внедрением зависимостей через конструктор; - срезы:
@WebMvcTestподнимает только веб-слой сMockMvc, здесь проверяются маршруты, валидация, коды ответов и JSON.@DataJpaTestподнимает только JPA и нужен для запросов репозиториев; - бины-заглушки в контексте:
@MockitoBeanиз Spring Framework 6.2, старый@MockBeanобъявлен устаревшим начиная со Spring Boot 3.4; - интеграционные тесты:
@SpringBootTestплюс Testcontainers с настоящими PostgreSQL и Kafka. Аннотация@ServiceConnection(со Spring Boot 3.1) сама прописывает адрес контейнера в настройки, без@DynamicPropertySource; - внешние HTTP-сервисы: WireMock, чтобы проверить тайм-ауты, повторы и срабатывание circuit breaker из Resilience4j;
- контракты: Spring Cloud Contract или Pact, чтобы изменения API не ломали потребителей незаметно;
- асинхронность: Awaitility вместо
Thread.sleepпри проверке событий из Kafka.
Про сборку: в Maven юнит-тесты запускает Surefire, а интеграционные с суффиксом IT запускает Failsafe на фазе integration-test, в Gradle под них заводят отдельную задачу или source set. Покрытие считает JaCoCo, но процент без проверки ошибочных сценариев ничего не гарантирует.
Где спотыкаются
Пишут всё через @SpringBootTest. Каждый класс тестов тянет полный контекст, и набор идёт десятки минут вместо пары.
Используют H2 вместо настоящей базы. Диалект, типы и блокировки отличаются, и нативный запрос или миграция Flyway ломаются только на стенде.
Раскидывают разные наборы @MockitoBean по классам. Spring кэширует контекст по конфигурации, и каждое новое сочетание заглушек заставляет поднимать его заново.