JavaRush /Курси /Модуль 5. Spring /Лекція 266: Вступ до контрактного тестування (Pact)

Лекція 266: Вступ до контрактного тестування (Pact)

Модуль 5. Spring
Рівень 21 , Лекція 5
Відкрита

Давайте одразу перейдемо до суті. Коли мікросервіси взаємодіють один з одним, вони стають як двоє розробників, які домовилися: "Я тобі відправлю файл у форматі JSON з такими полями!". У контрактному тестуванні цей "договор" називається контрактом. Ідея в тому, що взаємодії між сервісами перевіряються на відповідність заздалегідь заданим умовам, щоб упевнитися, що ніхто випадково не порушив цей "договор".

Приклад: Уявіть, що ви працюєте над мікросервісом "Замовник", який робить HTTP-запити до мікросервісу "Продукти". "Продукти" повертає у відповіді JSON з інформацією про товар. Ви можете написати контрактні тести, щоб гарантувати, що "Продукти" завжди повертає коректний JSON, який відповідає очікуванням "Замовника".

Чому це важливо?

У мікросервісній архітектурі, де кожен сервіс незалежний, але взаємодіє з іншими, оновлення однієї частини системи може легко зламати іншу. Наприклад, якщо команда розробників сервісу "Продукти" раптово змінить структуру JSON-відповіді, то сервіс "Замовник" почне падати з помилками. Контрактне тестування допомагає уникнути таких ситуацій:

  • Воно гарантує стабільність взаємодій між сервісами.
  • Воно економить час, оскільки проблеми з інтеграцією знаходять на ранніх етапах розробки.
  • Воно зменшує кількість ручних перевірок, бо взаємодії тестуються автоматично.

Різниця між інтеграційним і контрактним тестуванням

Інтеграційні тести перевіряють взаємодію кількох компонентів, а контрактні тести фокусуються на точній відповідності очікуваних вхідних і вихідних даних між двома сервісами. Контрактне тестування корисне тим, що дозволяє тестувати взаємодію в ізольованому оточенні, не піднімаючи обидва сервіси.


Основи роботи з Pact

Pact — це популярний інструмент для контрактного тестування. Він слугує посередником між двома сторонами:

  • Споживачем (consumer) — сервісом, який надсилає запити.
  • Провайдером (provider) — сервісом, який відповідає на запити.

Pact дозволяє створювати і перевіряти контракти між цими сторонами.

Основний принцип роботи Pact

  1. Створення контракту: споживач створює контракт, який описує, які запити він надсилає і які відповіді очікує від провайдера.
  2. Перевірка контракту: провайдер перевіряє контракт, щоб гарантувати, що він може правильно відповідати на запити споживача.

Приклад робочого потоку:

  1. Сервіс "Замовник" генерує контракт, де вказано:
    • Я надішлю GET-запит на /products/1
    • Я очікую у відповіді JSON з полями id, name, price
  2. Цей контракт передається сервісу "Продукти".
  3. Сервіс "Продукти" використовує Pact для перевірки, що він може відправити такий JSON.

Приклад використання Pact для мікросервісів

Перейдемо до практики. Уявімо, що в нас є два мікросервіси:

  • Consumer (споживач): сервіс "Замовник", який запитує інформацію про товари.
  • Provider (провайдер): сервіс "Продукти", який повертає інформацію про товари.

Крок 1: Створення контракту на боці споживача

Почнемо зі "Замовника". У цьому сервісі ми описуємо, який запит і відповідь очікуємо від "Продуктів".

Перед початком роботи додамо бібліотеку Pact у наш build.gradle:

dependencies {
    testImplementation 'au.com.dius.pact.consumer:junit5:4.5.7'
}

Написання тесту з використанням Pact


@PactTestFor(providerName = "ProductService")
public class ProductConsumerContractTest {

    @Pact(consumer = "CustomerService")
    public RequestResponsePact createPact(PactDslWithProvider builder) {
        return builder
                .given("Product with ID 1 exists")
                .uponReceiving("A request for product with ID 1")
                .path("/products/1")
                .method("GET")
                .willRespondWith()
                .status(200)
                .body("{\"id\": 1, \"name\": \"Laptop\", \"price\": 1200.00}")
                .toPact();
    }

    @Test
    @PactTestFor(pactMethod = "createPact")
    public void testConsumerBehaviour(MockServer mockServer) {
        // Використовуємо mockServer для емуляції API "Продуктів"
        String response = new RestTemplate().getForObject(mockServer.getUrl() + "/products/1", String.class);

        // Перевіряємо, що відповідь відповідає очікуванням
        assertEquals("{\"id\": 1, \"name\": \"Laptop\", \"price\": 1200.00}", response);
    }
}

Пояснення:

  • Ми створюємо контракт за допомогою Pact.
  • У контракті вказано, що споживач (CustomerService) робить GET-запит на /products/1 і очікує певну JSON-відповідь.
  • Pact піднімає MockServer для тестування поведінки споживача.

Крок 2: Перевірка контракту на боці провайдера

Тепер передамо контракт у сервіс "Продукти" і перевіримо, що він відповідає очікуванням.

Додамо Pact у провайдерський сервіс:

dependencies {
    testImplementation 'au.com.dius.pact.provider:junit5:4.5.7'
}

Перевірка контракту


@Provider("ProductService")
@PactBroker(host = "localhost", port = "9292")  // Pact broker для зберігання контрактів
public class ProductProviderContractTest {

    @TestTemplate
    @ExtendWith(PactVerificationInvocationContextProvider.class)
    public void validatePacts(PactVerificationContext context) {
        context.verifyInteraction();
    }

    @State("Product with ID 1 exists")
    public void productExists() {
        // Налаштовуємо тестову базу даних або моки для стану "Продукт з ID 1 існує"
    }
}

Пояснення:

  • Ми використовуємо Pact для перевірки контракту.
  • У методі productExists задаємо початковий стан (наприклад, додаємо запис у базу).
  • Pact автоматично перевіряє, що сервіс "Продукти" відповідає контракту.

Переваги використання Pact

  1. Раннє знаходження проблем: проблеми інтеграції між споживачем і провайдером одразу стають помітні.
  2. Ізоляція тестів: споживач і провайдер тестуються незалежно.
  3. Документація взаємодій: контракти служать авто-документацією API.

Висновок теми

Контрактне тестування — це супергерой мікросервісних взаємодій. Інструмент на кшталт Pact допомагає вашим командам спати спокійно, знаючи, що оновлення одного сервісу не зруйнують інший. На наступних лекціях ми заглибимося у практичне використання Pact і напишемо наші перші контрактні тести для взаємодії між мікросервісами.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ