Middle+
Разкажете за контрактното тестване на микросервиз.
sobes.tech AI
Отговор от AI
Контрактното тестване се фокусира върху проверката на взаимодействието между микросервисите на ниво техния API (контракт). Целта е да се уверим, че потребителят (сервиз, който използва API) и доставчикът (сервиз, който предоставя API) спазват определен "контракт" (схема на заявки/отговори, формат на данните, очаквания).
Основни принципи:
- Потребител-водещ: Тестовете се базират на очакванията на потребителя. Потребителят определя какви данни и формати му трябват.
- Двустранно тестване: Проверява се както страната на потребителя (може ли той да работи с текущия контракт), така и страната на доставчика (отговаря ли реализацията му на очакванията на потребителя).
- Бързина: Тестовете се изпълняват бързо, често в рамките на CI/CD pipeline, тъй като не изискват разгръщане на всички зависими услуги.
Процесът обикновено включва:
- Потребителят създава тестове, които описват очакванията си към API-то на доставчика. Тези тестове генерират "контракт".
- Контрактът се публикува (например, в централизирано хранилище).
- Доставчикът използва публикувания контракт за верификация на реализацията си на API. Той стартира тестове (обикновено от страната на доставчика), които проверяват дали неговият API съответства на контракта.
- В случай на несъответствие между очакванията на потребителя и реализацията на доставчика, тестът пада, сочейки нарушение на контракта.
Инструменти:
- Pact (най-популярен)
- Spring Cloud Contract
- Swagger/OpenAPI с инструменти за валидиране
Предимства:
- Ранно откриване на грешки при интеграция: Проблемите с съвместимостта се откриват още преди разгръщането.
- Намаляване на необходимостта от интеграционни тестове: Намалява зависимостта от сложни и бавни цялостни тестове.
- Независимо тестване: Позволява тестване на услугите независимо една от друга.
- Ясно определяне на очакванията: Ясно дефинира контракта между услугите.
Недостатъци:
- Не заменя напълно интеграционните тестове (само проверява взаимодействието на ниво API).
- Изисква дисциплина при версиониране на контрактите.
- Може да бъде по-сложно за услуги с много сложни контракти.
Пример за използване с Pact:
- Потребител (например, услуга
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)); } } - При изпълнение на тестовете от страна на клиента се генерира файл с контракт (
.json). - Този файл се публикува в Pact Broker или друго централизирано хранилище.
- Доставчикът (
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)); } } - Verifier-ът от страната на доставчика извиква реалния API на доставчика според спецификацията на контракта и сравнява получения отговор с очаквания в контракта.
Контрактното тестване е важна практика за осигуряване на надеждност и поддържане на системите базирани на микросервизи.