JavaRush /Курсы /Модуль 5. Spring /Лекция 262: Unit-тестирование для микросервисов: инструме...

Лекция 262: Unit-тестирование для микросервисов: инструменты и подходы

Модуль 5. Spring
27 уровень , 1 лекция
Открыта

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

Юнит-тесты помогают:

  • Убедиться, что базовая бизнес-логика работает корректно. Например, банковский сервис не должен просто взять и подарить клиенту пару миллионов.
  • Изолировать ошибки. Если тест упал, вы сразу видите, какой метод или модуль виноват.
  • Облегчить рефакторинг кода. Меняете реализацию? Убедитесь, что старые тесты всё ещё проходят.
  • Сохранить разработчика в здравии. Нет ничего хуже, чем отлавливать баг, который вылезет после деплоя.

Основные инструменты для юнит-тестирования

Для написания юнит-тестов в экосистеме Java вам не нужно изобретать велосипед. Всё уже давно придумано:

  1. JUnit 5 — это стандарт де-факто для тестирования в Java. Мощный, гибкий и довольно простой в освоении.
  2. Mockito — библиотека для создания "моков" (фиктивных объектов), которые позволяют тестировать модули в изоляции.
  3. AssertJ — для более читаемых и расширенных утверждений в тестах (необязательно, но полезно).

JUnit 5: основы тестирования

JUnit позволяет писать единичные тесты (юнит-тесты), которые проверяют конкретные методы или классы. Наиболее полезные аннотации JUnit:

  • @Test — помечает метод как тестовый.
  • @BeforeEach — выполняется перед каждым тестом.
  • @AfterEach — выполняется после каждого теста.
  • @DisplayName — задаёт читабельное имя для теста.

Пример простого теста с JUnit:


import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;

class CalculatorTest {

    @Test
    void testAddition() {
        Calculator calculator = new Calculator();
        int result = calculator.add(1, 2);
        assertEquals(3, result, "1 + 2 должно быть равно 3");
    }
}

Mockito: создание моков

Mockito — это ваш лучший друг для тестирования зависимостей. Моки позволяют изолировать тестируемый код, заменяя настоящие зависимости на поддельные. Они нужны, чтобы тестировать только ваш код, без вызова сторонних сервисов, баз данных и прочего.

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


import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.mockito.Mockito.*;

class OrderServiceTest {

    @Test
    void testCreateOrder() {
        // Создаём мок для репозитория
        OrderRepository repository = mock(OrderRepository.class);

        // Создаём OrderService с поддельным репозиторием
        OrderService service = new OrderService(repository);

        // Определяем поведение мока: возвращаем фиктивное значение
        when(repository.save(any(Order.class))).thenReturn(new Order(1L, "Test Order"));

        // Вызываем метод и проверяем результат
        Order order = service.createOrder("Test Order");
        assertEquals(1L, order.getId(), "ID заказа должен быть равен 1");
        assertEquals("Test Order", order.getName(), "Имя заказа должно быть 'Test Order'");

        // Проверяем, что метод save действительно был вызван
        verify(repository).save(any(Order.class));
    }
}

Подходы к написанию юнит-тестов

При тестировании сервисов вы, скорее всего, столкнётесь с различными зависимостями: репозитории, другие сервисы, внешние API. Чтобы тестировать логику изолированно, нам нужно:

  1. Создать моки для зависимостей.
  2. Задать их поведение с помощью методов when (например, when(repository.findById(1L)).thenReturn(mockEntity)).
  3. Проверить, вызывались ли нужные методы с правильными параметрами (используя verify).

Пример с несколькими зависимостями:


import static org.mockito.Mockito.*;

class PaymentServiceTest {
    @Test
    void testProcessPayment() {
        PaymentGateway gateway = mock(PaymentGateway.class);
        NotificationService notification = mock(NotificationService.class);

        PaymentService service = new PaymentService(gateway, notification);

        when(gateway.process(any(PaymentRequest.class))).thenReturn(true);

        service.processPayment(new PaymentRequest(100));

        verify(gateway).process(any(PaymentRequest.class));
        verify(notification).sendNotification(any());
    }
}

Тестирование бизнес-логики

Юнит-тесты должны проверять не только позитивные сценарии, но и граничные случаи. Например:

  • Проверка на ввод некорректных данных. Сервис должен выбросить исключение или вернуть ошибку.
  • Проверка на "пустые" данные: пустой список, нулевые значения и т.д.
  • Проверка на максимальные и минимальные значения входных параметров.

Пример:


@Test
void testValidateInput_NullValue() {
    IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
        someService.validateInput(null);
    });
    assertEquals("Input cannot be null", exception.getMessage());
}

Что тестировать, а что — нет?

Важно помнить, что юнит-тесты не должны:

  1. Тестировать сторонние библиотеки (вы уверены, что ваш StringUtils.capitalize() работает? Надеюсь, и так понятно).
  2. Тестировать взаимодействие с базой данных или сетью (для этого есть интеграционные тесты и Testcontainers).

Юнит-тесты должны:

  1. Тестировать логику вашего приложения: методы, функции, классы.
  2. Проверять граничные случаи: некорректные данные, крайние значения.
  3. Проверять, что вызовы зависимостей происходят именно так, как нужно (с помощью Mockito).

Типичные ошибки при написании юнит-тестов

  1. Тесты зависят от внешнего окружения. Например, если тест обращается к реальной базе данных.
  2. Отсутствие проверок. Если тест просто выполняется, но ничего не проверяет, это не тест.
  3. Путаница с моками. Слишком сложная настройка моков может запутать вас и ваших коллег.
  4. Тесты стали сложнее, чем сам код. Помните, что тесты должны быть простыми и понятными.

Практическое применение

Юнит-тесты не только помогут вам убедиться в работоспособности вашего кода, но и понравятся вашему будущему работодателю. Во время собеседования вас могут спросить: "Как вы пишете тесты?" И тут вы блеснёте своими знаниями JUnit, Mockito и стратегий тестирования.

Кроме того, качественные юнит-тесты делают изменения в коде намного безопаснее. Если кто-то из вашей команды случайно "сломает" бизнес-логику, тесты тут же об этом сообщат.

Ссылки на документацию:

Итак, берём JUnit, Mockito и начинаем писать тесты для нашего микросервисного приложения. Ведь только протестированный код — это хороший код!

Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ