Sobes.tech
Middle+

Vorbește despre testarea contractului unui microserviciu.

sobes.tech AI

Răspuns de la AI

Testarea contractelor se concentrează pe verificarea interacțiunii dintre microservicii la nivelul API-ului (contract). Scopul este de a asigura că consumatorul (serviciul care utilizează API-ul) și furnizorul (serviciul care oferă API-ul) respectă un "contract" anumit (schema cererii/răspunsului, formatul datelor, așteptările).

Principii principale:

  • Consumator-dirijat: Testele se bazează pe așteptările consumatorului. Consumatorul determină ce date și formate are nevoie.
  • Testare bidirecțională: Se verifică atât dacă partea consumatorului (poate lucra cu contractul actual), cât și partea furnizorului (dacă implementarea sa corespunde așteptărilor consumatorului).
  • Viteză: Testele se execută rapid, adesea în cadrul pipeline-urilor CI/CD, deoarece nu necesită implementarea tuturor serviciilor dependente.

Procesul include de obicei:

  1. Consumatorul creează teste care descriu așteptările sale de la API-ul furnizorului. Aceste teste generează un "contract".
  2. Contractul este publicat (de exemplu, într-un depozit centralizat).
  3. Furnizorul folosește contractul publicat pentru a-și verifica implementarea API-ului. Rulează teste (de obicei pe partea furnizorului) care verifică dacă API-ul său respectă contractul.
  4. În cazul diferențelor între așteptările consumatorului și implementarea furnizorului, testul eșuează, indicând o încălcare a contractului.

Instrumente:

  • Pact (cel mai popular)
  • Spring Cloud Contract
  • Swagger/OpenAPI cu instrumente de validare

Avantaje:

  • Depistarea timpurie a erorilor de integrare: Problemele sunt descoperite înainte de implementare.
  • Reducerea necesității testelor de integrare: Reduce dependența de teste complexe și lente end-to-end.
  • Testare independentă: Permite testarea serviciilor independent unele de altele.
  • Definirea clară a așteptărilor: Definește în mod explicit contractul între servicii.

Dezavantaje:

  • Nu înlocuiește complet testele de integrare (verifică doar interacțiunea la nivelul API-ului).
  • Necesită disciplină în gestionarea versiunilor contractelor.
  • Poate fi mai dificil pentru servicii cu contracte foarte complexe.

Exemplu cu Pact:

  1. Consumatorul (de exemplu, serviciul Order Service) folosește biblioteca Pact pentru a crea teste pe partea consumatorului.
    // Exemplu de test pe partea consumatorului cu 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 {
            // Logica de apel API
            HttpResponse response = Request.Get(mockServer.getUrl() + "/products/1").execute().returnResponse();
            assertThat(response.getStatusLine().getStatusCode(), equalTo(200));
        }
    }
    
  2. La rularea testelor pe partea consumatorului, se generează un fișier de contract (.json).
  3. Acest fișier de contract este publicat în Pact Broker sau într-un depozit centralizat.
  4. Furnizorul (Product Service) folosește biblioteca Pact (de exemplu, Pact Provider Verifier) pentru a verifica implementarea API-ului său pe baza contractului primit din Pact Broker.
    // Exemplu de test pentru furnizor cu Pact Provider Verifier
    @Provider("ProductService")
    @PactFolder("relative/path/to/pacts") // sau folosește 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() {
            // Logica de pregătire a stării datelor în serviciul furnizor pentru test
        }
    
        // Adaugă URL-ul serviciului unde rulează serviciul testat
        @BeforeEach
        void beforeEach(PactVerificationContext context) {
            context.setTarget(new HttpTestTarget("localhost", 8080));
        }
    }
    
  5. Verificatorul apelează API-ul real al furnizorului conform specificației contractului și compară răspunsul primit cu cel așteptat în contract.