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:
- Consumatorul creează teste care descriu așteptările sale de la API-ul furnizorului. Aceste teste generează un "contract".
- Contractul este publicat (de exemplu, într-un depozit centralizat).
- 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.
- Î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:
- 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)); } } - La rularea testelor pe partea consumatorului, se generează un fișier de contract (
.json). - Acest fișier de contract este publicat în Pact Broker sau într-un depozit centralizat.
- 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)); } } - Verificatorul apelează API-ul real al furnizorului conform specificației contractului și compară răspunsul primit cu cel așteptat în contract.