Elke service apart testen, zonder gedeelde testomgeving en zonder orkestratieprobleem. Dat is wat contract testing oplevert in een microservices architectuur, en het werkt anders dan de meeste teams denken.
Bij veel teams draait de integratietest nog altijd op hetzelfde recept: de hele stack opstarten, hopen dat alles samenwerkt, en drie uur wachten op een falende pipeline. Contract testing doorbreekt dat.
Met Pact test je niet of alles samen draait, maar of elke service zich houdt aan wat de andere ervan verwacht. In seconden, niet in minuten.
Het principe draait om drie stappen.
Service A, de consumer, legt vast welke request hij stuurt en welke response hij verwacht.
Dat contract wordt gepubliceerd naar een centrale opslagplaats, de Pact Broker.
Service B, de provider, voert de contracten uit als test, zonder dat service A daarvoor actief hoeft te zijn.
Het resultaat: elke service wordt onafhankelijk getest, zonder gedeelde testomgeving en zonder orkestratieprobleem.
Stel: de checkout-service (consumer) roept de user-service (provider) op voor klantgegevens. Zo ziet dat contract er in de praktijk uit.
// legt vast welke request/response checkout-service verwacht
const { PactV3 } = require('@pact-foundation/pact');
const provider = new PactV3({ consumer: 'checkout-service', provider: 'user-service' });
provider
.given('gebruiker 123 bestaat')
.uponReceiving('een aanvraag voor gebruiker 123')
.withRequest({ method: 'GET', path: '/users/123' })
.willRespondWith({
status: 200,
body: { id: 123, name: 'Jelle', email: 'jelle@qadvice.be' }
});
it('haalt de gebruiker op', () =>
provider.executeTest(async (mockserver) => {
const res = await fetch(`${mockserver.url}/users/123`);
expect(res.status).toBe(200);
})
);
Deze test genereert automatisch het contract en publiceert het naar de Pact Broker:
// ingekort contract, opgeslagen in de Pact Broker
{
"consumer": { "name": "checkout-service" },
"provider": { "name": "user-service" },
"interactions": [{
"description": "een aanvraag voor gebruiker 123",
"request": { "method": "GET", "path": "/users/123" },
"response": {
"status": 200,
"body": { "id": 123, "name": "Jelle", "email": "jelle@qadvice.be" }
}
}]
}
De provider verifieert dat contract zonder dat checkout-service ooit moet draaien:
// speelt alle gepubliceerde contracten af tegen de echte provider
const { Verifier } = require('@pact-foundation/pact');
new Verifier({
provider: 'user-service',
providerBaseUrl: 'http://localhost:3001',
pactBrokerUrl: 'https://pact.qadvice.be',
publishVerificationResult: true,
providerVersion: '1.4.2'
}).verifyProvider();
Geeft user-service het veld email niet meer terug? Dan faalt de verificatie meteen, en weet je exact welke service en welk veld het probleem zijn, zonder dat checkout-service ooit moest opstarten.
Faalt een contracttest, dan is meteen duidelijk welke service welk veld niet meer levert en wie dat moet fixen. De Pact Broker biedt bovendien een can-i-deploy check: is het veilig om deze versie te releasen, gegeven alle contracten die ermee samenhangen? Het resultaat is minder flaky tests, minder wantrouwen in de suite, en een snellere releasecyclus.
Contract testing is sterk voor REST API's, dus GET en POST interfaces tussen services, en werkt ook voor async messaging zoals Kafka, RabbitMQ of SNS/SQS. Voor complexe, stateful multi-step interacties is het minder geschikt. En voor kritieke gebruikersflows blijft end-to-end testen nodig, contract testing vervangt dat niet.
Contracten die niet mee-evolueren met de code geven een vals gevoel van veiligheid. Een contract dat niemand nog bijwerkt, test niets zinvols meer.
Over-specificeren maakt de provider onnodig fragiel. Test enkel wat de consumer echt gebruikt, niet elk veld dat toevallig in de response zit.
Contract testing valideert interfaces, geen business logica. Hou een slanke end-to-end suite voor de paden die er echt toe doen.
Contract testing werkt het best als aanvulling op een doordachte teststrategie, niet als vervanging ervan. Minder end-to-end, meer vertrouwen, snellere releases: dat is wat het oplevert wanneer je het goed inzet.
We nemen je testaanpak door en zeggen eerlijk waar de winst zit. En waar niet.
Plan een gesprek