Trendsz, Test engineering, Testautomatisering, Tooling

In deel 1 heb ik je een introductie in de wereld van contract-based testen gegeven. Vervolgens ben ik in deel 2 en deel 3 uitgebreid ingegaan op waarom je contract-based testen boven end-to-end testen zou moeten kiezen. In dit deel — deel 4 — maak ik een begin met hoe contract-based testen werkt. Ook ga ik in op de verschillende benaderingen van contract-based testen.

In deel 3 vertelde ik je al dat we bij contract-based testen elke applicatie koppelen met één bestand: een contract. Het contract is de enige koppeling tussen de applicaties. Vervolgens wordt dit contract gebruikt om onze applicatie in isolatie te testen.

CBT1

Centrale contract repository

Contract-based testen werkt alleen als zowel de provider als de consumer hetzelfde contract gebruiken. Daarom moet het delen van die contracten goed aangepakt worden.

Om contracten effectief te delen, wordt een centrale contract repository gebruikt. Dit is een centrale opslagplaats waar iedereen contracten kan ontdekken, publiceren en downloaden. De centrale contract repository is de enige infrastructuur die een organisatie nodig heeft om contract-based te testen, ongeacht de schaal.

De contract repository is essentieel om ervoor te zorgen dat iedereen altijd de laatste contracten gebruikt. Contracten delen via de mail, chat of een ander medium dan de centrale repository is dus geen goed idee!

Waar te beginnen?

Bij contract-based testen moeten er voor elke integratie twee dingen worden gemaakt; de integratie en het contract. Wanneer we beginnen met het schrijven van het contract, noemen we het contract-first. Als we in plaats daarvan beginnen met het bouwen van de integratie, noemen we het — heel creatief — implementation-first.

CBT2

Bij contract-based testen is het altijd beter om contract-first te werken. Een contract is een manier om specificaties op te schrijven. Door met het contract te beginnen, kunnen we het contract tijdens de development-cyclus als specificatie­document gebruiken. Tegelijkertijd kunnen we de computer-leesbare karakter van contracten gebruiken om testen te maken voor de interface. Het contract is hiermee een fundamenteel onderdeel van onze tests. Dit helpt ons om snel kwaliteit te leveren.

Voor bestaande interfaces werk je per definitie implementation-first. In deze manier van werken kan je niet profiteren van een aantal procesvoordelen die contract-first wel biedt, maar je kunt nog steeds contract-based testen. Hoe dit precies werkt, hangt af van de benadering van je contract.

Twee benaderingen van contract-based testen

In deel 1 noemde ik al dat er twee manieren zijn om het contract te benaderen; provider-driven en consumer-driven. Het is eindelijk tijd om deze benaderingen van contract-based testen beter te bekijken.

Provider-driven contract-based testen

In de provider-driven benadering schrijft de provider het contract. De provider zorgt ervoor dat het contract overeenkomt met de implementatie. Als ze zeker zijn van het contract, delen ze het contract met alle consumers. De interface consumers gebruiken het contract als onderdeel van hun tests.

CBT3

Als er meerdere consumers zijn, gebruiken ze allemaal hetzelfde provider­contract. Dit maakt deze benadering perfect voor interfaces met meerdere consumers.

CBT4

Het contract

Een provider-driven contract bevat de definitie en documentatie van de interface. Het kan alleen worden aangepast door de provider. Het beschrijft alle technische details zoals endpoints, HTTP-statuscodes, response-body-structuur, enzovoorts.

Technische details zijn mooi voor computers, maar computers zullen niet de enigen zijn die het contract gebruiken. Om ervoor te zorgen dat ook mensen het effectief kunnen gebruiken, bevat het contract voorbeelden en beschrijvingen. Hierdoor lijkt het schrijven van een provider­contract veel op het schrijven van de interface­documentatie, alleen dan in een gestandaardiseerd formaat.

Een kwalitatief provider-driven contract …

  • Omvat de hele interface. Het laat niets weg.
  • Is zo specifiek mogelijk zodat er geen ruimte is voor aannames. Let op dat dit geldt voor zowel mensen als computers. Vooral mensen staan bekend om het maken van verkeerde aannames, maar ook computers doen dit weleens via onverwachte defaults.
  • Houdt zich aan alle standaarden en richtlijnen. Denk aan de contractstandaard, additionele bedrijfsstandaarden en algemene best practices.

Waar te beginnen?

Door contract-first te werken, kunnen we het contract vanaf het begin gebruiken als een fundamenteel onderdeel van de tests van onze interface. Hiermee kan de provider elk verschil tussen het contract en de interface afvangen tijdens de development-cyclus van de interface. Elk verschil zal resulteren in gefaalde test aan de kant van de provider.

Voor bestaande interfaces is het over het algemeen het beste om het contract te genereren op basis van de implementatie­code. Hierdoor kan de consumer de meeste voordelen van het contract plukken, terwijl de provider amper iets hoeft te veranderen. Echter, het genereren van een contract komt met de prijs van een minder kwalitatief contract, vooral voor mensen.

Beide benaderingen zoals hier beschreven zorgen ervoor dat de implementatie en het contract altijd op elkaar zijn uitgelijnd. Dit is absoluut essentieel, omdat het de consument in staat stelt veilig aan te nemen dat het contract de waarheid beschrijft.

Verantwoordelijkheden van de provider

In de provider-driven benadering heeft de provider op elk moment de volledige controle over alle aspecten van de interface. “With great power comes great responsibility”. De verantwoordelijkheden van de provider omvatten:

  1. Het contract schrijven
    Onderdeel hiervan is dat de provider ervoor moet zorgen dat het contract van hoge kwaliteit is. Een contract van hoge kwaliteit leidt direct tot contract­tests van hoge kwaliteit voor zowel de provider als de consumer.
  1. De interface bouwen
    Logisch: dat is de definitie van wat een provider is.
  1. Het contract uitlijnen op de interface
    De uitlijning tussen de interface implementatie en het contract dat deze beschrijft, is essentieel. Hierdoor kunnen consumers veilig aannemen dat het contract de realiteit van de implementatie beschrijft. Bij het overslaan van deze uitlijning zal de consumer testen met slechte stubs, wat tot productie­problemen zal leiden.
  1. Publiceren van het contract
    De laatste verantwoordelijkheid van de provider is om het uitgelijnde, kwalitatieve contract in de centrale contract repository te publiceren.
CBT5

Verantwoordelijkheden van de consumer

Bij de provider-driven benadering begint de consumer met een aanname: het contract beschrijft de interface correct. De consumer is verantwoordelijk voor het testen en implementeren van de integratie aan hun kant op basis van het contract. De verantwoordelijkheden van de consumer omvatten:

  1. Downloaden van het laatste contract
    Bij elke test run downloadt de consumer het laatste contract van de centrale contract repository. Dit zorgt ervoor dat ze nooit testen met een verouderd contract.
  1. Consumeren zoals beschreven in het contract
    Bij het consumeren mag de consumer nooit afwijken van het contract. Alles wat niet in het contract wordt beschreven, kan op elk moment zonder voorafgaande kennisgeving worden gewijzigd. Wanneer je ongedocumenteerd gedrag tegenkomt, is het redelijk om de provider te vragen dit in het contract op te nemen. Een onvolledig contract is een kwaliteits­probleem waarvoor de provider verantwoordelijk is.
  1. Alle stubs maken
    Om ervoor te zorgen dat hun applicatie werkt zoals verwacht, schrijft de consumer tests. Deze tests bevatten stubs voor de interface. Hierdoor kunnen de tests in een geïsoleerde omgeving worden uitgevoerd. De consumer baseert deze stubs op het contract.
  1. Valideren stubs tegen het contract
    Validatie van de stubs verzekerd een bepaald kwaliteits­niveau en het voorkomt stub-drift. Ofwel, het voorkomt dat de stubs stilletjes afdrijven van de realiteit van de productie. Nadat de stubs zijn gevalideerd, kunnen ze worden gebruikt in de consumers tests zoals normale stubs. Als je dit doet, worden de meeste problemen aan de consumer-kant opgevangen voordat ze de productie bereiken.
CBT6

Voordelen

De provider-driven benadering is een goede manier om technische correctheid en ‑uitlijning te garanderen. Dit klinkt misschien erg beperkt, maar de praktijk leert dat de meeste tests in deze categorie vallen. Bij deze tests hebben we de volledige controle over alle onderdelen, inclusief (gestubde) afhankelijkheden. We hoeven niet te wachten op andere teams en we kunnen onze tests uitvoeren zonder een speciale omgeving. Door alles lokaal te doen, kun je de tests snel maken en uitvoeren.

Het schalen van het aantal consumers is bij deze benadering eenvoudig. Alle consumers gebruiken hetzelfde contract en de provider heeft volledige controle over zowel het contract en de interface. Alle communicatie over de interface verloopt via het contract. Dit alles maakt de benadering ideaal voor interfaces met meerdere consumers. Het betekent ook dat een nieuwe consumer niet met de provider hoeft te communiceren, hij kan alles zelf doen op basis van het contract. Dit maakt de provider-driven benadering ideaal voor op het internet beschikbare interfaces.

Nadelen

De provider-driven benadering heeft ook zo z’n minpunten. Zo kunnen we geen functionele tests tussen applicaties uitvoeren, omdat data nooit de geïsoleerde test­omgevingen zullen verlaten. Bovendien kan het feit dat de provider volledige controle heeft over de interface vervelend zijn voor consumers. Vooral omdat de provider meestal niet echt weet wat de consumer van de interface nodig heeft. De provider-driven benadering is eenrichtings­communicatie van de provider naar de consumer. Hierdoor voelt een provider-driven contract aan als terms and conditions: accept, or get out!

Conclusie

Of je nou voor de provider- of consumer-driven benadering van contract-based testen kiest, het is altijd beter om contract-first te werken. Zorg er bij het delen van contracten voor dat je dit uitsluitend doet via een centrale contract repository.

CBT7

In deze blog hebben we het voornamelijk over de provider-driven benadering gehad. Bij deze benadering begint alles bij de provider. Ze zijn verantwoordelijk voor het schrijven van een uitgelijnd, kwalitatief contract. Ze delen het contract met hun consumers via de centrale contract repository. De consumers downloaden het laatste contract bij elke test run en gebruiken deze om hun stubs te valideren.

In het volgende deel zal ik het hebben over de consumer-driven benadering. Ik vergelijk dan ook de voor- en nadelen van de twee benaderingen met elkaar.

Met dank aan Vilas Pultoo voor de illustraties

Schrijf je in voor
de nieuwsbrief

werkenbij overall

Werken bij Bartosz?

Vincent Verhelst

Geïnteresseerd in Bartosz? Dan ga ik graag met jou in gesprek. We kunnen elkaar ontmoeten met een kop koffie bij ons op kantoor. Of tijdens ontbijt, lunch, borrel of diner op een plek die jou het beste uitkomt. Jij mag het zeggen.

Mijn Paarsz