Sobes.tech
Middle+

Povězte o kontraktovém testování mikroservisu.

sobes.tech AI

Odpověď od AI

Testování podle smlouvy se zaměřuje na kontrolu interakce mezi mikroservisy na úrovni jejich API (smlouva). Cílem je zajistit, že spotřebitel (služba využívající API) a dodavatel (služba poskytující API) dodržují určitý "smluvní" (schéma požadavku/odpovědi, formát dat, očekávání).

Hlavní principy:

  • Spotřebitel-řízené: Testy jsou založeny na očekáváních spotřebitele. Spotřebitel určuje, jaká data a formáty potřebuje.
  • Obousměrné testování: Kontroluje se jak, zda strana spotřebitele (umí pracovat s aktuální smlouvou), tak i strana dodavatele (odpovídá jeho implementace očekáváním).
  • Rychlost: Testy se provádějí rychle, často v rámci CI/CD pipeline, protože nevyžadují nasazení všech závislých služeb.

Proces obvykle zahrnuje:

  1. Spotřebitel vytváří testy popisující jeho očekávání od API dodavatele. Tyto testy generují "smlouvu".
  2. Smlouva je publikována (například v centralizovaném úložišti).
  3. Dodavatel používá publikovanou smlouvu k ověření své implementace API. Spouští testy (obvykle na straně dodavatele), které kontrolují, zda jeho API odpovídá smlouvě.
  4. V případě odchylek mezi očekáváními spotřebitele a realizací dodavatele, test selže, což signalizuje porušení smlouvy.

Nástroje:

  • Pact (nejpopulárnější)
  • Spring Cloud Contract
  • Swagger/OpenAPI s validačními nástroji

Výhody:

  • Včasné odhalení chyb integrace: Problémy jsou odhaleny ještě před nasazením.
  • Snížení potřeby integračních testů: Snižuje závislost na složitých a pomalých end-to-end testech.
  • Nezávislé testování: Umožňuje testovat služby nezávisle na sobě.
  • Jasné stanovení očekávání: Výslovně definuje smlouvu mezi službami.

Nevýhody:

  • Nezastupuje úplně integrační testy (kontroluje pouze interakci na úrovni API).
  • Vyžaduje disciplínu v verzování smluv.
  • Může být složitější pro služby s velmi složitými smlouvami.

Příklad použití s Pact:

  1. Spotřebitel (například, Order Service) používá knihovnu Pact k vytváření testů na straně spotřebitele.
    // Příklad testu spotřebitele s 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 volání API
            HttpResponse response = Request.Get(mockServer.getUrl() + "/products/1").execute().returnResponse();
            assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
        }
    }
    
  2. Při spuštění testů na straně spotřebitele je generována smluvní soubor (.json).
  3. Tento soubor smlouvy je publikován do Pact Brokeru nebo jiného centralizovaného úložiště.
  4. Dodavatel (Product Service) používá knihovnu Pact (například Pact Provider Verifier) k ověření své implementace API na základě smlouvy získané z Pact Brokeru.
    // Příklad testu dodavatele s Pact Provider Verifier
    @Provider("ProductService")
    @PactFolder("relative/path/to/pacts") // nebo použít 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 přípravy stavu dat v službě dodavatele pro test
        }
    
        // Přidání URL adresy služby, kde běží testovaná služba
        @BeforeEach
        void beforeEach(PactVerificationContext context) {
            context.setTarget(new HttpTestTarget("localhost", 8080));
        }
    }
    
  5. Verifikátor volá skutečné API dodavatele podle specifikace smlouvy a porovnává obdrženou odpověď s očekávanou ve smlouvě.