Middle+
Vertel over contract testing van een microservice.
sobes.tech AI
Antwoord van AI
Contract testing richt zich op het controleren van de interactie tussen microservices op API-niveau (contract). Het doel is om te verzekeren dat de consument (service die API gebruikt) en de leverancier (service die API aanbiedt) zich aan een bepaald "contract" (verzoek/antwoord schema, datavormaat, verwachtingen) houden.
Belangrijke principes:
- Consument-gedreven: Tests zijn gebaseerd op de verwachtingen van de consument. De consument bepaalt welke gegevens en formaten hij nodig heeft.
- Tweerichtings testing: Controleert zowel of de consument (of hij met het huidige contract kan werken) als de leverancier (of zijn implementatie aan de verwachtingen voldoet).
- Snelheid: Tests worden snel uitgevoerd, vaak binnen CI/CD pipelines, omdat ze niet alle afhankelijke services hoeven te implementeren.
Het proces omvat meestal:
- De consument maakt tests die zijn verwachtingen van de API beschrijven. Deze tests genereren een "contract".
- Het contract wordt gepubliceerd (bijvoorbeeld in een gecentraliseerde repository).
- De leverancier gebruikt het gepubliceerde contract om zijn API-implementatie te verifiëren. Hij voert tests uit (meestal aan de leverancierzijde) die controleren of zijn API aan het contract voldoet.
- Bij afwijkingen tussen de verwachtingen van de consument en de implementatie van de leverancier, faalt de test en wordt de contractbreuk aangegeven.
Tools:
- Pact (populairst)
- Spring Cloud Contract
- Swagger/OpenAPI met validatietools
Voordelen:
- Vroege detectie van integratiefouten: Problemen worden al vóór deployment ontdekt.
- Vermindering van integratietests: Vermindert afhankelijkheid van complexe en langzame end-to-end tests.
- Onafhankelijke testing: Maakt het mogelijk om services onafhankelijk van elkaar te testen.
- Duidelijke definitie van verwachtingen: Definieert expliciet het contract tussen services.
Nadelen:
- Vult niet volledig de integratietests (controleert alleen interactie op API-niveau).
- Vereist discipline in versiebeheer van contracten.
- Kan moeilijker zijn voor services met zeer complexe contracten.
Voorbeeld met Pact:
- De consument (bijvoorbeeld,
Order Service) gebruikt de Pact-bibliotheek om consumer-side tests te maken.// Voorbeeld van een consumer-side test met 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-aanroep logica HttpResponse response = Request.Get(mockServer.getUrl() + "/products/1").execute().returnResponse(); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); } } - Bij het uitvoeren van consumer-side tests wordt een contractbestand (
.json) gegenereerd. - Dit contractbestand wordt gepubliceerd naar Pact Broker of een andere gecentraliseerde repository.
- De leverancier (
Product Service) gebruikt de Pact Provider Verifier bibliotheek om zijn API-implementatie te verifiëren op basis van het contract dat uit de Pact Broker is gehaald.// Voorbeeld van een leverancierstest met Pact Provider Verifier @Provider("ProductService") @PactFolder("relative/path/to/pacts") // of gebruik 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 voor het voorbereiden van de data-omgeving voor de test } // Voeg de URL van de service toe waar de test wordt uitgevoerd @BeforeEach void beforeEach(PactVerificationContext context) { context.setTarget(new HttpTestTarget("localhost", 8080)); } } - De verifier roept de echte API van de leverancier aan volgens de specificatie van het contract en vergelijkt het ontvangen antwoord met wat in het contract is verwacht.