Sobes.tech
Middle+

дар бораи санҷиши шартномаи микросервис гӯед.

sobes.tech AI

Ҷавоб аз AI

Тестування на основі контракту зосереджено на перевірці взаємодії між мікросервісами на рівні їх API (контракт). Мета — переконатися, що споживач (сервіс, який використовує API) і постачальник (сервіс, який надає API) дотримуються певного "контракту" (схеми запитів/відповідей, формату даних, очікувань).

Основні принципи:

  • Споживач-орієнтований: Тести базуються на очікуваннях споживача. Споживач визначає, які дані і формати йому потрібні.
  • Двостороннє тестування: Перевіряється, чи може сторона споживача працювати з поточним контрактом, і чи відповідає реалізація сторони постачальника очікуванням споживача.
  • Швидкість: Тести виконуються швидко, часто у рамках CI/CD, оскільки не вимагають розгортання всіх залежних сервісів.

Процес зазвичай включає:

  1. Споживач створює тести, що описують його очікування від API постачальника. Ці тести генерують "контракт".
  2. Контракт публікується (наприклад, у централізованому репозиторії).
  3. Постачальник використовує опублікований контракт для верифікації своєї реалізації API. Він запускає тести (зазвичай на стороні постачальника), які перевіряють, чи відповідає його API контракту.
  4. У разі розбіжностей між очікуваннями споживача і реалізацією постачальника, тест падає, вказуючи на порушення контракту.

Інструменти:

  • Pact (найпопулярніший)
  • Spring Cloud Contract
  • Swagger/OpenAPI з інструментами валідації

Переваги:

  • Раннє виявлення помилок інтеграції: Проблеми сумісності виявляються ще до розгортання.
  • Зменшення необхідності інтеграційних тестів: Знижує залежність від складних і повільних сквозних тестів.
  • Незалежне тестування: Дозволяє тестувати сервіси незалежно один від одного.
  • Чітке визначення очікувань: Явно визначає контракт між сервісами.

Недоліки:

  • Не замінює повністю інтеграційні тести (тільки перевіряє взаємодію на рівні API).
  • Вимагає дисципліни у версіонуванні контрактів.
  • Може бути складніше для сервісів з дуже складними контрактами.

Приклад використання з Pact:

  1. Споживач (наприклад, сервіс Order Service) використовує бібліотеку Pact для створення тестів з боку споживача.
    // Приклад тесту споживача з Pact JVM
    @ExtendWith(PactConsumerTestExt.class)
    @PactTestFor(providerName = "ProductService", port = "8080")
    public class ProductServiceContractTest {
    
        @Pact(consumer = "OrderService")
        public RequestResponsePact createPact(PactDslWithRequest r) {
            return r.given("a product with id 1 exists")
                    .uponReceiving("a request for product by id")
                    .path("/products/1")
                    .method("GET")
                    .willRespondWith()
                    .status(200)
                    .headers(Map.of("Content-Type", "application/json"))
                    .body(new PactDslJsonBody()
                            .stringValue("id", "1")
                            .stringValue("name", "Laptop")
                            .numberType("price", 1200.00))
                    .toPact();
        }
    
        @Test
        void testGetProductById(MockServer mockServer) throws IOException {
            // Логіка виклику API постачальника через mockServer
            // Перевірка, що споживач правильно обробля мок-відповідь
            HttpResponse response = Request.Get(mockServer.getUrl() + "/products/1").execute().returnResponse();
            assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
        }
    }
    
  2. При виконанні тестів споживача генерується файл контракту (.json).
  3. Цей файл публікується у Pact Broker або інше централізоване сховище.
  4. Постачальник (Product Service) використовує бібліотеку Pact (наприклад, Pact Provider Verifier) для верифікації своєї реалізації API на основі контракту, отриманого з Pact Broker.
    // Приклад тесту постачальника з Pact Provider Verifier
    @Provider("ProductService")
    @PactFolder("relative/path/to/pacts") // або використання Pact Broker
    public class ProductServiceVerificationTest {
    
        @TestTemplate
        @ExtendWith(PactVerificationInvocationContextProvider.class)
        public void verifyPact(PactVerificationContext context) {
            context.verifyInteraction();
        }
    
        @State("a product with id 1 exists")
        public void productWithId1Exists() {
            // Логіка підготовки стану даних у сервісі постачальника для тесту
        }
    
        // Додаємо URL постачальника, де запущена тестована служба
        @BeforeEach
        void beforeEach(PactVerificationContext context) {
            context.setTarget(new HttpTestTarget("localhost", 8080));
        }
    }
    
  5. Верифікатор зі сторони постачальника викликає реальний API постачальника згідно з специфікацією контракту і порівнює отриману відповідь з очікуваною у контракті.

Контрактне тестування є важливою практикою для забезпечення надійності та підтримуваності систем на базі мікросервісів.