Sobes.tech
Middle+

Разкажете за контрактното тестване на микросервиз.

sobes.tech AI

Отговор от AI

Контрактното тестване се фокусира върху проверката на взаимодействието между микросервисите на ниво техния API (контракт). Целта е да се уверим, че потребителят (сервиз, който използва API) и доставчикът (сервиз, който предоставя API) спазват определен "контракт" (схема на заявки/отговори, формат на данните, очаквания).

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

  • Потребител-водещ: Тестовете се базират на очакванията на потребителя. Потребителят определя какви данни и формати му трябват.
  • Двустранно тестване: Проверява се както страната на потребителя (може ли той да работи с текущия контракт), така и страната на доставчика (отговаря ли реализацията му на очакванията на потребителя).
  • Бързина: Тестовете се изпълняват бързо, често в рамките на CI/CD pipeline, тъй като не изискват разгръщане на всички зависими услуги.

Процесът обикновено включва:

  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. Verifier-ът от страната на доставчика извиква реалния API на доставчика според спецификацията на контракта и сравнява получения отговор с очаквания в контракта.

Контрактното тестване е важна практика за осигуряване на надеждност и поддържане на системите базирани на микросервизи.