Als er meerdere consumers zijn, dan schrijven ze allemaal een contract. In de consumer-driven benadering zorgt dit voor schalingsproblemen. Deze vermijden we in de bi-directional benadering door te testen met het provider contract in plaats van met de provider applicatie. Door beide contracten te gebruiken, voorkomen we de meeste problemen die normaal gepaard gaan met het gebruik van één contractsoort. Ze vullen elkaar aan en creëren op die manier een schaalbare, supersnelle benadering met procesvoordelen.
Verantwoordelijkheden van de provider
De verantwoordelijkheden van de provider zijn bijna identiek aan die in de provider-driven benadering, namelijk:
- Schrijven van het contract: Zorg ervoor dat het contract van hoge kwaliteit is. Een contract van hoge kwaliteit leidt direct tot contracttests van hoge kwaliteit. Een provider contract van hoge kwaliteit is zo specifiek mogelijk, voor zowel mensen als computers.
- Verbinding maken met de interface: Logisch, dat is de definitie van wat een provider is.
- Het contract uitlijnen op de interface: De uitlijning tussen de interface implementatie en het contract dat deze beschrijft, is essentieel. Hierdoor kunnen we het contract gebruiken als een analoog voor de provider applicatie.
- Publiceren van het contract: Publiceer het uitgelijnde, kwalitatieve contract in de centrale contract repository. Hierna kan het contract getest worden met de consumer contracten.
- Luisteren naar testresultaten: De centrale contract repository test alle contracten met elkaar. Als deze test faalt weten we dat de provider op het punt staat om een breaking change te introduceren voor minstens één consumer. De provider moet niet releasen als dit gebeurt omdat consumers issues zullen hebben in productie.
Verantwoordelijkheden van de consumer
De verantwoordelijkheden van de consumer zijn bijna identiek aan die in de consumer-driven benadering, namelijk:
- Schrijven van het contract: Onderdeel hiervan is ervoor te zorgen dat het contract van hoge kwaliteit is. Een kwalitatief contract leidt direct tot hoogwaardige contracttesten en een gestroomlijnde interface. Een consumer contract van hoge kwaliteit bevat zo min mogelijk terwijl het wel alles bevat wat de consumer applicatie actief gebruikt.
- Verbinding maken met de interface: Logisch, dat is de definitie van wat een consumer is.
- Testen van de consumer applicatie met het contract: De consumer zorgt ervoor dat zijn applicatie alles aankan wat in hun consumer contract is gedefinieerd. Hierdoor kunnen we het contract gebruiken als een analoog voor de consumer applicatie.
- Publiceren van het contract: Publiceer het geteste, kwalitatieve contract in de centrale contract repository. Hierna kan het contract getest worden met het provider contract.
- Luisteren naar testresultaten: De centrale contract repository test alle contracten met elkaar. Als deze test faalt weten we dat de consumer op het punt staat om een breaking change te introduceren voor zichzelf. De consumer moet niet releasen als dit gebeurt omdat ze issues zullen hebben in productie.
Verantwoordelijkheden van de centrale contract repository
Elke benadering van contract-based testen heeft een centrale contract repository nodig om contracten op te slaan. De contract repository zorgt ervoor dat iedereen de nieuwste contracten gebruikt. De bi-directional benadering voegt hier een verantwoordelijkheid aan toe: testen of een set contracten samen werken.
De contract repository heeft al toegang tot alle contracten. Dit maakt de contract repository ideaal gepositioneerd om contracten tegen elkaar te testen. Het centraliseren van deze functionaliteit zorgt er ook voor dat elke test wordt uitgevoerd met dezelfde software. Dat kan een hoop hoofdpijn schelen. Een test gestart door de provider heeft altijd dezelfde uitkomst als een test gestart door een consumer. De centrale contract repository is de autoriteit die bepaald of een aanpassing veilig naar productie kan.
Samengevat
Alle betrokken partijen schrijven op wat ze verwachten van een integratie in de vorm van een contract. De centrale contract repository kijkt of de verwachtingen op elkaar aansluiten. De consumer schrijft een zo simpel mogelijk kwalitatief consumer contract zoals ze dat zouden doen bij de consumer-driven benadering. Ze uploaden hun contract naar de centrale contract repository en de contract repository vertelt de consumer of hun contract werkt met het provider contract. En de provider schrijft een zo simpel uitgelijnd kwalitatief provider contract zoals ze dat zouden doen bij de provider-driven benadering. Ze uploaden hun contract naar de centrale contract repository en de contract repository vertelt de provider of hun contract werkt met alle consumer contracten.
Voordelen
Beide kanten van de integratie schrijven een contract vanuit hun perspectief. Dit levert voordelen op. Ten eerste, het voelt simpel. Alle betrokkenen schrijven op wat ze verwachten van de integratie. Een tool kijkt of alle verwachtingen werken met elkaar. Je hoeft niet eens precies te begrijpen hoe de tool dat doet, zolang je luistert naar de uitkomst. Een ander voordeel is dat we data kunnen halen uit de contracten. Deze data is nuttig voor documentatie, het evolueren van de integratie, audits en meer. Heb je ooit de behoefte gehad om een netwerkgrafiek te maken van welke applicatie met wie is verbonden? Dat kan met de data in de contracten.
Nog een ander voordeel is dat de contracttesten in de contract repository statische testen zijn. Dit zijn testen die geen applicatiecode uit hoeven te voeren. Hierdoor zijn deze testen sneller dan zelfs unit testen. Dit verkort de feedbackloop, wat erg belangrijk is voor de developer experience.
Daarnaast is de bi-directional aanpak een goede manier om technische correctheid en ‑uitlijning te garanderen aan beide kanten van de integratie. De aanpak doet dit op een snelle, schaalbare manier. Het is even schaalbaar als de provider-driven aanpak. Tegelijkertijd behoudt het veel van de procesvoordelen die inherent zijn aan de consumer-driven aanpak. Op veel manieren is de bi-directional aanpak het beste van beide werelden.
Anders dan in de provider-driven en consumer-driven aanpak, heeft niemand volledige controle in de bi-directional aanpak. Beide kanten moeten zich houden aan de contracten aan de andere kant van de integratie. Dit creëert een omgeving waar incrementele aanpassingen de norm zijn en de teams met elkaar praten voordat er een breaking-change wordt doorgevoerd. De meeste nadelen die inherent zijn aan de consumer-driven aanpak zijn niet aanwezig in de bi-directional aanpak, ondanks het gebruik van consumer contracten. Het opschalen van het aantal testen of consumers is geen probleem. De consumers kunnen ook niet de release van de provider blokkeren met het schrijven van een verkeerd consumer contract.
Nadelen
Omdat het een gecombineerde aanpak is, heeft de bi-directional aanpak minder nadelen. De provider-driven aanpak fixt de meeste nadelen die inherent zijn aan de consumer-driven aanpak en vice versa. Een belangrijk nadeel is dat we alleen technische tests kunnen uitvoeren met de bi-directional aanpak. Er zijn geen opties voor functionele tests over meerdere applicaties omdat data nooit de centrale contract repository zal verlaten. Verder is de bi-directional aanpak ongeveer net zo strikt als de provider-driven aanpak is. Dit is goed genoeg voor de meeste situaties, maar zal niet altijd genoeg zijn.
Een striktere variant
De bi-directional aanpak is ongeveer net zo strikt als de provider-driven aanpak, maar niet zo strikt als de consumer-driven aanpak. Meestal is dit goed genoeg. Maar, als het nodig is, kun je de bi-directional aanpak strikter maken door het uit te bereiden met de provider kant van de consumer-driven aanpak.
De striktere variant werkt door twee keer te testen met de consumer contracten. Eerst testen we of de consumer contracten werken met het provider contract (zie ook mijn eerder verschenen blogreeks). Als de eerste test slaagt, testen we of de contracten werken met de provider applicatie. Let wel op dat je hiermee de nadelen die kleven aan de consumer-driven aanpak opnieuw introduceert. De belangrijkste nadelen zijn dat het moeilijk opschaalt en dat het mogelijk is om de release aan de provider kant te blokkeren door het schrijven van een verkeerd consumer contract.
Conclusie
In de bi-directional aanpak van contract-based testen schrijven de provider en alle consumers een contract vanuit hun perspectief. De centrale contract repository test of alle contracten samen kunnen werken.
Deze gecombineerde aanpak garandeert technische correctheid op een manier die de meeste nadelen inherent aan provider-driven en consumer-driven aanpakken mitigeert. Het resultaat is een zeer krachtige aanpak op zowel technisch- als proces vlak waar geen van de partijen een disproportionele hoeveelheid macht heeft over de integratie. Het combineren van de twee contracttypes leidt tot een aanpak die makkelijk te begrijpen is en minder nadelen heeft. Provider contracten en consumer contracten werken dus beter als ze samen worden gebruikt.
Meer weten over contract-based testen?
Lees dan de eerder verschenen blogreeks van Sander of luister zijn podcast waarin hij jou wegwijs maakt in de wereld van contract-based testen. Onder andere vertelt hij uitgebreid over zijn ervaringen, legt hij uit wanneer contract-based testen wordt ingezet, en geeft hij aan wat de te verwachtte trends zijn. Je luistert de podcast via ons SoundCloud account.