Alle artikels

Een contract is een gesprek, geen schema

Contract testing wordt meestal verkocht als een testtechniek. Je legt het schema van een bericht vast, genereert een mock en laat een pipeline controleren of producer en consumer zich eraan houden.

Dat klopt, maar het mist het belangrijkste. De grootste waarde van een contract zit niet in de geautomatiseerde test. Ze zit in het gesprek dat nodig is om het contract op te stellen.

Het symptoom: de hele keten deployen om één service te testen

Herkenbaar patroon: een team wil zijn consumer-service testen. Daarvoor deployt het de producer mee, en alles wat daartussen zit. Dan start het een flow aan het begin van de keten en kijkt manueel of het juiste event binnenkomt.

Dat is traag, fragiel en afhankelijk van de staat van andere teams. Is de dev-omgeving van de producer net kapot, dan kan de consumer niet testen.

De reflex is om dit technisch op te lossen: betere testomgevingen, meer automatisering. Maar de vraag is waarom het team de producer überhaupt nodig heeft.

De wortel: impliciete afspraken

Het consumerende team deployt de producer omdat het niet expliciet weet wat er kan binnenkomen. Het moet de echte producer zien draaien om het te weten. Het deployen is een symptoom van niet-weten.

Dat patroon staat zelden alleen. Late integratie, testers die niet weten wat ze testen en uitlopende testfases hebben vaak dezelfde oorzaak: afspraken tussen domeinen bestaan in hoofden, niet als vindbaar artefact.

zichtbaar onder de oppervlakte Hele keten deployenom één service te testenLate integratieproblemen pas op het eindeTesters zonder contextweten niet wat te testenUitlopende testfasesde planning schuift op Afspraken bestaan in hoofden niet als vindbaar artefact
Wat je ziet zijn de symptomen. Ze delen één wortel die onder de oppervlakte zit.

Een typische oorzakenketen: een integratietest loopt uit omdat het schema niet geversioneerd is. Dat komt omdat er nog functionele feedback verwacht wordt. En die komt er niet, omdat het gesprek met de business nog niet heeft plaatsgevonden. De zichtbare pijn ligt vier stappen van de wortel. Meer tooling raakt die wortel niet.

Een contract is breder dan een schema

Een AsyncAPI- of Avro-schema beschrijft de vorm van een bericht. Een volwaardig contract gaat verder: welke flows worden ondersteund, welke use cases zitten erachter, wat betekent elk veld voor de business. Het schema is de formele neerslag van die afspraak, niet de afspraak zelf.

Daarom kan een ontwikkelaar een contract niet alleen opstellen. Er zijn drie rollen nodig:

Rol Brengt in
Functioneel analist De flows, use cases en business-betekenis
Tester Wat getest moet worden, en het contract als referentie
Ontwikkelaar De technische vorm, en de vertaling naar code

Het samenbrengen van die drie is de eigenlijke interventie. De geautomatiseerde test die er later op draait, is grotendeels een bijproduct.

Van gesprek naar test: één gedeeld feature file

Een schema legt de structuur vast. De betekenis leg je vast in gedragsscenario's, in Gherkin. Het scenario beschrijft wat er over de grens tussen twee systemen gebeurt:

Feature: Productieorder afronden
  Scenario: Afgewerkte order boekt voorraad af
    Given een productieorder voor 500 kg van materiaal X
    When het productiesysteem de order als afgewerkt meldt
    Then boekt het voorraadsysteem 500 kg van materiaal X af

Dit scenario is de neerslag van het gesprek tussen analist, tester en ontwikkelaar. Het staat in een gedeelde repository, naast het AsyncAPI-contract. Geen van beide teams is eigenaar; het is een gezamenlijke afspraak.

Elk team implementeert alleen zijn eigen helft, met eigen step definitions:

Gedeeld feature file Given een productieorder When order afgewerkt Then voorraad afgeboekt gedeelde repository Productiesysteemimplementeert Given en When Voorraadsysteemimplementeert Then AsyncAPI-contractgenereert de mock toetst het event mock levert het event
Eén afspraak, twee helften. Elk team draait zijn deel geïsoleerd in CI; op UAT loopt hetzelfde scenario over de echte keten.
  • De producer implementeert Given en When tegen zijn eigen service, en toetst het uitgaande event aan het contract.
  • De consumer krijgt het event van een mock die uit het contract gegenereerd is, en implementeert Then tegen zijn eigen service.
  • In CI draait elk team zijn helft geïsoleerd. Niemand heeft de service van het andere team nodig.
  • Op UAT draait hetzelfde scenario end-to-end over de echte keten. Daar vang je de bedrading en de semantiek die isolatie niet dekt.

Het feature file overleeft technologiewissels. Vervangt een team zijn service, dan herschrijft het alleen zijn step definitions. Het scenario, en dus de afspraak, blijft staan.

Twee soorten flows, twee soorten winst

Wat een contract oplevert, hangt af van de staat van de flow.

Flow Aanpak Winst
Bestaand en stabiel Contract afleiden uit het bestaande verkeer, daarna laten bevestigen Ontkoppeling: de consumer test geïsoleerd tegen een mock
Nog in functionele beweging Contract opstellen terwijl de afspraak ontstaat Preventie: het ontbreken van een afspraak wordt vroeg zichtbaar

Beloof die twee niet door elkaar. Bij een stabiele flow werkt een contract meteen. Bij een flow in beweging dwingt het gesprek de afspraak naar voren, maar het maakt die afspraak niet zelf.

Grenzen en valkuilen

Een contract lost niet alles op. Sommige grenzen accepteer je bewust:

  • Vorm is geen betekenis. Twee domeinen kunnen hetzelfde schema respecteren en toch iets anders bedoelen met een veld. Dat blijf je pas in een integratie- of acceptatietest zien.
  • Bedrading test je op de echte keten. Listener-configuratie, topic-koppeling en serialisatie horen op een gedeelde testomgeving.

Andere risico's kun je beheersen:

  • Waargenomen is niet bedoeld. Een contract afgeleid uit bestaand verkeer kan een fout tot norm verheffen. Laat een analist bevestigen dat het contract ook het bedoelde gedrag beschrijft.
  • Stille drift. Zonder versionering en compatibiliteitschecks blijven tests groen tegen een verouderd contract. Dat is erger dan geen contract: het geeft vals vertrouwen.
  • Het gesprek sterft na de eerste sessie. Dit is het grootste risico, en het is niet technisch. Houd het proces licht en herhaalbaar, en begin met één bewezen voorbeeld.

Afsluiting

Contract testing invoeren als tool is relatief eenvoudig. Het levend houden van de afspraak tussen analist, tester en ontwikkelaar is het moeilijke deel. Dat is waar vergelijkbare initiatieven doorgaans falen, niet op de tooling.

Begin daarom niet bij de tool, maar bij één bestaande integratie en één gesprek. Leg vast wat er werkelijk tussen twee domeinen stroomt en wat het betekent. De mock, de test en de ontkoppeling volgen daarna vanzelf.

Loop je tegen een gelijkaardig probleem aan? Ik denk graag mee.

Neem contact op