Питання 25 · Розділ 17

Як тестувати мікросервіси

Structured Java interview answer with junior, middle, and senior-level explanation.

Мовні версії: English Russian Ukrainian

🟢 Junior Level

Три рівні тестування:

  1. Unit тести — тестування одного класу/методу
  2. Integration тести — тестування взаємодії з БД, зовнішніми сервісами
  3. Contract тести — перевірка сумісності API між сервісами
// Unit тест
@Test
void testOrderCalculation() {
    OrderService service = new OrderService();
    Order order = service.createOrder(List.of(item1, item2));
    assertEquals(100, order.total());
}

🟡 Middle Level

Integration тести

@SpringBootTest
@Testcontainers
class OrderServiceIntegrationTest {

    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");

    @Test
    void testCreateOrder() {
        Order order = orderService.createOrder(request);
        assertNotNull(order.getId());
    }
}

Contract тести (Pact)

@Provider("Order Service")
@Consumer("User Service")
public class OrderServiceContractTest {
    @TestTemplate
    void testGetUserOrders(PactVerificationContext context) {
        // Interaction = один запит-відповідь між consumer і provider.
        // Pact перевіряє, що provider може обробити запит, який очікує consumer.
        // Contract тест ловить breaking changes в API до потрапляння в production.
        context.verifyInteraction();
    }
}

Типові помилки

  1. Тести з реальними сервісами:
    Мережевий виклик → реальний Payment Service → повільно (100-500ms на запит)
    і нестабільно (мережа може впасти, сервіс може бути недоступний).
    Рішення: mock або testcontainers
    

🔴 Senior Level

Test pyramid

     /  E2E    \      — мало (10%)
    / Integration \   — середньо (20%)
   /    Unit       \  — багато (70%)

Consumer-driven contracts

User Service (consumer) визначає контракт → Order Service (provider) перевіряє

Production Experience

Testcontainers:

@Testcontainers
class UserRepositoryTest {
    @Container
    static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");

    // @DynamicPropertySource (Spring Boot 2.2.6+) — динамічно передає порти
    // контейнера в Spring Environment до ініціалізації контексту.
    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
    }
}

Best Practices

✅ Test pyramid
✅ Testcontainers для інтеграції
✅ Contract тести для API
✅ Mock зовнішніх сервісів
✅ CI/CD pipeline з тестами

❌ Тільки E2E тести
❌ Тести з реальними сервісами
❌ Без contract тестів

🎯 Шпаргалка для співбесіди

Обов’язково знати:

  • Test pyramid: Unit (70%) → Integration (20%) → E2E (10%)
  • Unit тести — один клас/метод, швидко, ізольовано
  • Integration тести — взаємодія з БД, зовнішніми сервісами (Testcontainers)
  • Contract тести (Pact) — перевірка сумісності API між сервісами, ловлять breaking changes
  • Testcontainers — Docker-контейнери для інтеграційних тестів (PostgreSQL, Kafka)
  • Consumer-driven contracts: consumer визначає контракт, provider перевіряє
  • Mock зовнішніх сервісів — не тестувати з реальними (повільно, нестабільно)

Часті уточнюючі питання:

  • Навіщо contract тести? Ловлять breaking changes в API до потрапляння в production — consumer очікує X, provider має віддати X.
  • Testcontainers vs mock? Testcontainers = реальний сервіс у Docker (надійніше), mock = заглушка (швидше).
  • Чому не тільки E2E? E2E повільно, крихкі, складно дебажити. Unit швидше, стабільніше.
  • @DynamicPropertySource що робить? Динамічно передає порти контейнера в Spring Environment до ініціалізації контексту.

Червоні прапорці (НЕ говорити):

  • “Тільки E2E тести достатньо” — ні, повільно, крихкі, складно дебажити
  • “Тести з реальним Payment Service” — ні, повільно (100-500ms) і нестабільно
  • “Contract тести не потрібні, є Swagger” — Swagger описує API, contract тести перевіряють сумісність
  • “Unit тести = інтеграційні” — ні, unit = один клас, integration = взаємодія

Пов’язані теми:

  • [[26. Які інструменти використовуються для оркестрації мікросервісів]]
  • [[15. Як організувати комунікацію між мікросервісами]]
  • [[22. Що таке distributed tracing]]
  • [[9. Що таке API Gateway і які завдання він вирішує]]
  • [[17. Як забезпечити відмовостійкість мікросервісів]]