Якщо порівняти розробку мікросервісів із зведенням хмарочоса, то можна сказати, що unit-тести — це перевірка надійності кожної цегляної кладки. Уяви, що одна цеглина виявилася бракованою: хмарочос може впасти. З мікросервісами та сама історія — навіть невелика помилка в сервісі може призвести до великих проблем на етапі інтеграції або експлуатації.
Unit-тести допомагають:
- Переконатися, що базова бізнес-логіка працює коректно. Наприклад, банківський сервіс не має просто так подарувати клієнту пару мільйонів.
- Ізолювати помилки. Якщо тест впав, ти одразу бачиш, який метод або модуль винен.
- Полегшити рефакторинг коду. Змінюєш реалізацію? Переконайся, що старі тести все ще проходять.
- Зберегти розробника в здравії. Нічого гіршого, ніж відлавлювати баг, який виліз після деплоя.
Основні інструменти для unit-тестування
Щоб писати unit-тести в екосистемі Java, не треба винаходити велосипед. Все вже давно придумано:
- JUnit 5 — це де-факто стандарт для тестування в Java. Потужний, гнучкий і доволі простий у вивченні.
- Mockito — бібліотека для створення "моків" (фіктивних об'єктів), які дозволяють тестувати модулі в ізоляції.
- AssertJ — для більш читабельних і розширених assertions в тестах (необов'язково, але корисно).
JUnit 5: основи тестування
JUnit дозволяє писати одиничні тести (unit-тести), які перевіряють конкретні методи або класи. Найкорисніші анотації 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));
}
}
Підходи до написання unit-тестів
Під час тестування сервісів ви, скоріше за все, зіткнетесь з різними залежностями: репозиторії, інші сервіси, зовнішні API. Щоб тестувати логіку ізольовано, нам потрібно:
- Створити моки для залежностей.
- Задати їхню поведінку за допомогою методів
when(наприклад,when(repository.findById(1L)).thenReturn(mockEntity)). - Перевірити, чи викликалися потрібні методи з правильними параметрами (використовуючи
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());
}
}
Тестування бізнес-логіки
Unit-тести повинні перевіряти не лише позитивні сценарії, а й граничні випадки. Наприклад:
- Перевірка вводу некоректних даних. Сервіс має кинути виняток або повернути помилку.
- Перевірка "порожніх" даних: порожній список, нульові значення тощо.
- Перевірка максимальних і мінімальних значень вхідних параметрів.
Приклад:
@Test
void testValidateInput_NullValue() {
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> {
someService.validateInput(null);
});
assertEquals("Input cannot be null", exception.getMessage());
}
Що тестувати, а що — ні?
Важливо пам'ятати, що unit-тести не повинні:
- Тестувати сторонні бібліотеки (ти впевнений, що твій
StringUtils.capitalize()працює? Сподіваюся, і так зрозуміло). - Тестувати взаємодію з базою даних або мережею (для цього є інтеграційні тести і Testcontainers).
Unit-тести повинні:
- Тестувати логіку твого застосунку: методи, функції, класи.
- Перевіряти граничні випадки: некоректні дані, крайні значення.
- Перевіряти, що виклики залежностей відбуваються саме так, як потрібно (за допомогою Mockito).
Типові помилки при написанні unit-тестів
- Тести залежать від зовнішнього оточення. Наприклад, якщо тест звертається до реальної бази даних.
- Відсутність перевірок. Якщо тест просто виконується, але нічого не перевіряє, це не тест.
- Плутанина з моками. Забагато складної налаштування моків може заплутати тебе і твоїх колег.
- Тести стали складніші, ніж сам код. Пам'ятай, що тести мають бути простими й зрозумілими.
Практичне застосування
Unit-тести не лише допоможуть тобі переконатися в працездатності коду, але й сподобаються твоєму майбутньому роботодавцю. Під час співбесіди можуть спитати: "Як ти пишеш тести?" І тут ти зможеш вразити знаннями JUnit, Mockito і стратегій тестування.
Крім того, якісні unit-тести роблять зміни в коді значно безпечнішими. Якщо хтось з твоєї команди випадково "зламає" бізнес-логіку, тести одразу про це повідомлять.
Посилання на документацію:
Отже, беремо JUnit, Mockito і починаємо писати тести для нашого мікросервісного застосунку. Адже лише протестований код — це хороший код!
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ