Middle+
Opowiedz o testowaniu kontraktowym mikroserwisu.
sobes.tech AI
Odpowiedź od AI
Testy kontraktowe koncentrują się na weryfikacji interakcji między mikroserwisami na poziomie ich API (kontrakt). Celem jest upewnienie się, że konsument (serwis korzystający z API) i dostawca (serwis udostępniający API) przestrzegają określonego "kontraktu" (schemat zapytań/odpowiedzi, format danych, oczekiwania).
Podstawowe zasady:
- Konsument-kierowany: Testy opierają się na oczekiwaniach konsumenta. Konsument określa, jakie dane i formaty są mu potrzebne.
- Testy dwukierunkowe: Sprawdzana jest zarówno zdolność konsumenta (czy potrafi pracować z aktualnym kontraktem), jak i dostawcy (czy spełnia oczekiwania konsumenta).
- Szybkość: Testy są wykonywane szybko, często w ramach pipeline CI/CD, ponieważ nie wymagają wdrożenia wszystkich zależnych usług.
Proces zazwyczaj obejmuje:
- Konsument tworzy testy opisujące jego oczekiwania wobec API dostawcy. Testy te generują "kontrakt".
- Kontrakt jest publikowany (np. w repozytorium centralnym).
- Dostawca używa opublikowanego kontraktu do weryfikacji własnej implementacji API. Uruchamia testy (zazwyczaj po stronie dostawcy), które sprawdzają, czy jego API jest zgodne z kontraktem.
- W przypadku rozbieżności między oczekiwaniami konsumenta a implementacją dostawcy, test kończy się niepowodzeniem, wskazując na naruszenie kontraktu.
Narzędzia:
- Pact (najpopularniejszy)
- Spring Cloud Contract
- Swagger/OpenAPI z narzędziami walidacyjnymi
Zalety:
- Wczesne wykrywanie błędów integracji: Problemy z kompatybilnością wykrywane są jeszcze przed wdrożeniem.
- Zmniejszenie potrzeby testów integracyjnych: Redukuje zależność od skomplikowanych i wolnych testów end-to-end.
- Niezależne testy: Pozwala na testowanie serwisów niezależnie od siebie.
- Jasne określenie oczekiwań: Wyraźnie definiuje kontrakt między serwisami.
Wady:
- Nie zastępuje w pełni testów integracyjnych (sprawdza tylko interakcję na poziomie API).
- Wymaga dyscypliny w wersjonowaniu kontraktów.
- Może być trudniejsze dla serwisów z bardzo skomplikowanymi kontraktami.
Przykład użycia z Pact:
- Konsument (np. serwis
Order Service) używa biblioteki Pact do tworzenia testów po stronie konsumenta.// Przykład testu konsumenta z 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 { // Logika wywołania API dostawcy przez mockServer // Sprawdzanie, czy konsument poprawnie obsługuje mockowaną odpowiedź HttpResponse response = Request.Get(mockServer.getUrl() + "/products/1").execute().returnResponse(); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); } } - Podczas wykonywania testów po stronie konsumenta generowany jest plik kontraktu (
.json). - Plik ten jest publikowany w Pact Broker lub innym centralnym repozytorium.
- Dostawca (
Product Service) używa biblioteki Pact (np. Pact Provider Verifier) do weryfikacji własnej implementacji API na podstawie kontraktu pobranego z Pact Broker.// Przykład testu dostawcy z Pact Provider Verifier @Provider("ProductService") @PactFolder("relative/path/to/pacts") // lub użyć 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() { // Logika przygotowania stanu danych w serwisie dostawcy do testu } // Dodanie URL dostawcy, na którym działa testowana usługa @BeforeEach void beforeEach(PactVerificationContext context) { context.setTarget(new HttpTestTarget("localhost", 8080)); } } - Weryfikator po stronie dostawcy wywołuje rzeczywiste API dostawcy zgodnie z specyfikacją kontraktu i porównuje otrzymaną odpowiedź z oczekiwaną w kontrakcie.
Testy kontraktowe są ważną praktyką zapewniającą niezawodność i łatwość utrzymania systemów opartych na mikroserwisach.