Тестирование и Spring Boot Actuator

Код без тестов — мина замедленного действия. Spring Boot даёт срезы для быстрых тестов, а Actuator показывает здоровье приложения в проде.
Суть: юнит-тесты проверяют логику с Mockito без Spring. Срезы (@WebMvcTest, @DataJpaTest) поднимают часть контекста. @SpringBootTest — полный интеграционный тест. Actuator открывает health и метрики.

Тесты — это не бюрократия, а страховка от регрессий. Меняя код, вы хотите быть уверены, что не сломали старое. Spring Boot делает тестирование быстрым за счёт «срезов» — поднятия только нужной части приложения.

Юнит-тест с Mockito

Благодаря конструкторному внедрению сервис можно создать обычным new, подсунув ему моки зависимостей:

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock UserRepository repo;
    @InjectMocks UserService service;

    @Test
    void returnsUserWhenExists() {
        when(repo.findById(7L))
            .thenReturn(Optional.of(new User(7L, "Анна")));

        var result = service.findById(7L);

        assertEquals("Анна", result.name());
        verify(repo).findById(7L);
    }
}

Здесь Spring вообще не поднимается — тест быстрый. Это и есть награда за конструкторное внедрение из раздела про DI.

Срезы и интеграционные тесты

АннотацияЧто поднимает
@WebMvcTestТолько веб-слой (контроллеры, без БД)
@DataJpaTestТолько слой данных (репозитории + тестовая БД)
@SpringBootTestВесь контекст — полный интеграционный тест

Spring Boot Actuator

Actuator — это набор готовых эндпоинтов для эксплуатации: /actuator/health (живо ли приложение), /actuator/metrics (метрики), /actuator/info. Их используют системы мониторинга и оркестраторы (Kubernetes) для проверки готовности.

management:
  endpoints:
    web:
      exposure:
        include: health, info, metrics
  endpoint:
    health:
      show-details: when-authorized

Как работает под капотом

  Пирамида тестов и Actuator
  ┌──────────────────────────────────┐
  │ @SpringBootTest  (мало, медленно) │ весь контекст
  ├──────────────────────────────────┤
  │ @WebMvcTest / @DataJpaTest        │ срез слоя
  ├──────────────────────────────────┤
  │ юнит-тесты + Mockito (много)      │ без Spring
  └──────────────────────────────────┘

  Actuator в проде:
   Kubernetes --GET /actuator/health--> приложение
              <--  {"status":"UP"}  --  жив, шлём трафик

Смоделируем мок-зависимость и health-проверку:

# Мок репозитория + health-check, как Mockito и Actuator
class MockRepo:
    def __init__(self, data):
        self.data = data
        self.calls = []
    def find_by_id(self, id):
        self.calls.append(id)            # verify(repo).findById(...)
        return self.data.get(id)

def service_find(repo, id):
    user = repo.find_by_id(id)
    if user is None:
        raise ValueError("Не найден")
    return user

repo = MockRepo({7: {"id": 7, "name": "Анна"}})
print("Тест:", service_find(repo, 7))
print("Репозиторий вызван с:", repo.calls)   # [7]

def health_check(db_ok, disk_ok):
    status = "UP" if db_ok and disk_ok else "DOWN"
    return {"status": status, "components": {"db": db_ok, "disk": disk_ok}}

print("Health:", health_check(True, True))
print("Health:", health_check(False, True))

Нажмите «Попробуй сам ▶»: мок фиксирует вызовы (как verify), а health-check агрегирует состояние подсистем — как Actuator.

Частые ошибки

  • Только @SpringBootTest везде. Полный контекст медленный; для слоя берите срез.
  • Открыть все эндпоинты Actuator наружу. Часть из них чувствительна — ограничивайте доступ.
  • Тесты, зависящие от порядка. Каждый тест должен быть независимым и воспроизводимым.

Best practices

  • Стройте пирамиду: много юнит-тестов, меньше срезов, единичные @SpringBootTest.
  • Используйте конструкторное внедрение — оно делает юнит-тесты тривиальными.
  • Включайте Actuator health для проб готовности, но защищайте чувствительные эндпоинты.

Итог: тестируйте пирамидой — юнит-тесты с Mockito, срезы для слоёв, интеграционные для целого. Actuator даёт готовые health и метрики для эксплуатации в проде.

Проверьте себя
1. Какой тип теста стоит выбрать для быстрой проверки бизнес-логики сервиса в изоляции?
A@SpringBootTest с полным контекстом
BЮнит-тест с Mockito, создавая сервис через new и подсовывая моки
CТолько ручное тестирование
D@DataJpaTest
2. Для чего нужен эндпоинт /actuator/health?
AДля удаления данных
BЧтобы системы мониторинга и оркестраторы проверяли, живо ли приложение
CДля ускорения запросов
DДля генерации документации