Hva er Contract Testing og i hvilke situasjoner kan Contract Testing hjelpe oss?
Contract Testing er en metode for programvaretesting som fokuserer på å verifisere samspillet mellom forskjellige komponenter, tjenester eller systemer i en distribuert arkitektur. Denne tilnærmingen er gunstig i scenarier der flere tjenester eller komponenter utvikles og vedlikeholdes av separate team, og det er avgjørende å sikre at de kommuniserer og fungerer sammen korrekt. Kort sagt er Contract Testing en metode for å sikre at to separate systemer (som for eksempel to mikrotjenester) er kompatible og kan kommunisere med hverandre.
Fra en tidligere rolle i et team som utviklet et backend-system med flere grensesnitt, både med front-end og med andre interne og eksterne parter, har jeg mye erfaring med denne typen testing. For å kunne jobbe sammen om samme produkt, men uavhengig av hverandre, opprettet vi disse kontraktene, slik at vi ble enige om hva vi skulle utvikle før vi utviklet det. Dette betydde at begge parter involvert kunne lage mock-ups basert på kontrakten og bygge og teste uavhengig om den andre parten var ferdig med sin utvikling.
I Contract Testing er en kontrakt en formell spesifikasjon av forventet atferd og kommunikasjonsregler mellom to eller flere komponenter, tjenester eller systemer. Den definerer inndata, utdata og samhandlinger for å sikre at de involverte partene oppfyller hverandres forventninger. En kontrakt består vanligvis av følgende elementer:
Ved å tydelig definere kontrakten i Contract Testing, kan utviklingsteam opprette tester for å verifisere at hver komponent eller tjeneste overholder den spesifiserte atferden og kommunikasjonsreglene. Dette bidrar til å sikre en smidig integrasjon og korrekt fungering av det overordnede systemet.
Når man søker på internett, finner man raskt forkortelsene CDCT og PDCT, som står for Consumer Driven Contract Testing og Provider Driver Contract Testing. CDCT betyr Contract Testing fra synspunktet til grensesnittets forbruker. Den forbrukende parten angir sine behov og forventninger, og leverandøren eller utgiveren må sørge for at disse behovene og forventningene blir oppfylt. I dette tilfellet er forbrukeren i førersetet når det gjelder å definere grensesnittkontrakten. Når PDCT brukes, oppretter leverandøren (ofte kalt utgiver) kontrakten, og forbrukerne må forholde seg til det leverandøren tilbyr. Typen Contract Testing jeg har vært involvert i under min siste oppgave, var en der både forbruker og leverandør kommuniserte mye for å få den beste og mest effektive løsningen for begge parter. Ingen av partene var i ledelsen eller presset gjennom krav.
Tenk deg en stor e-commerce plattform som er utviklet ved hjelp av en mikrotjenestearkitektur. Plattformen består av forskjellige tjenester som brukerstyring, lagerstyring, ordrebehandling, betalingsbehandling og forsendelsessporing, hver administrert av et separat utviklingsteam. Tjenestene kommuniserer med hverandre via API-er og har godt definerte kontrakter som spesifiserer inndata, utdata og oppførsel for hver interaksjon.
I dette scenariet kan Contract Testing være svært gunstig:
Oppsummert er Contract Testing gunstig i scenarier som mikrotjenestearkitekturer, der flere tjenester eller komponenter samhandler, og det å sikre at disse samhandlingene fungerer som de skal, er avgjørende for påliteligheten og ytelsen til det overordnede systemet. Det er imidlertid ikke begrenset til mikrotjenestearkitekturer. Alle situasjoner der team må opprette eller oppdatere et grensesnitt, kan dra nytte av å lage klare kontrakter. På den måten vet alle involverte parter hva de kan forvente, og endringer som bryter med kontrakten kan knyttes til den avtalte kontrakten.
Tenk at vi har et enkelt Wallet API, med ett endepunkt der vi kan sette inn penger i lommeboken vår. Vårt /deposit-endepunkt har følgende JSON-request body:
{
"amount": 10.05
}
Parameteren "amount" har en verdi på 10.05 og er derfor et desimaltall med 2 sifret presisjon, så en "double" som det kalles i OpenAPI 3.0-dokumenter.
Vi bestemmer at parameteren "amount" er nødvendig og må være et positivt tall med en minimumsverdi på 0,01. Hvis dette ikke er tilfelle, skal det oppstå en feil med HTTP-kode 400.
Vi bestemmer også at det innskutte beløpet skal øke lommebokens saldo med samme beløp.
/deposit-endepunktet vil svare med en 200 (suksess) kode, og følgende JSON-response body:
{
"balance": 10.05
}
Parameteren "balance" har også en eksempelverdi på 10,05 og vil derfor også bli definert som en "double" i vår spesifikasjon.
Vi bestemmer at lommebokens "balance" aldri må være et negativt beløp.
Nedenfor finner du et eksempel på en OpenAPI 3.0-spesifikasjon for dette enkle Wallet API-et. Denne API-en har ett enkelt endepunkt (/deposit) som lar en bruker sette inn penger i lommeboken og returnere den oppdaterte lommeboksaldoen. Jeg liker personlig å bruke swagger.io for å modellere kontraktene mine. Den visuelle visningen av OpenAPI 3.0-spesifikasjoner er også veldig nyttig for å gjøre kontrakten lesbar. Mange verktøy (inkludert Confluence) tilbyr plugin-moduler for denne typen dokumentasjon, slik at det kan vises i sin visuelle tilstand, noe som gjør kontrakten mer lesbar for alle involverte parter.
Innenfor denne kontrakten/spesifikasjonen kan vi definere følgende elementer:
Dette enkle Wallet API-et demonstrerer hvordan man inkluderer forutsetninger, dataformater, postbetingelser, invariante betingelser og feilhåndtering i en OpenAPI 3.0-kontrakt. Hvis to team skulle implementere dette API-et (et publiserende team som lager dette API-et, og et konsumerende team som vil bruke dette API-et i applikasjonen deres), kan de nå jobbe separat fordi kontrakten er definert og avtalt. Begge teamene kan individuelt lage simulerte forespørsler og svar basert på denne kontrakten og sørge for at deres del av programvaren fungerer før de faktiske komponentene kobles sammen.
Se nedenfor den mer "lesbare" visningen av denne kontrakten, som genereres av swagger.io-redigeringsprogrammet.
Hva gjør vi med disse kontraktene når det gjelder testing? En måte å gå fram på er å manuelt sjekke om en forespørsel eller respons overholder den avtalte kontrakten. Men dette er en tidkrevende og kjedelig måte å gjøre det på, og det er ineffektivt å gjøre dette for hånd. Mange verktøy tilbyr nå muligheter for å sjekke en request eller response mot et skjema. Dette finnes både for SOAP og REST-tjenester. I tilleg finnes det onlineverktøy der du kan legge inn en request-/response body og et skjema, der verktøyet validerer om forespørselen/ responsen overholder dette skjemaet. Men det er enda bedre å bruke dette i dine automatiserte tester, for eksempel å legge dem til i Postman-testsettene dine ved å bruke for eksempel Ajv skjema validering.
Når man bruker skjemavalidering, ser vi tre mulige resultater:
a. Både request- og response body overholder den avtalte kontrakten
b. Request body overholder ikke den avtalte kontrakten
c. Response body overholder ikke den avtalte kontrakten
I scenario b, ligger problemet hos forbrukeren av tjenesten. Forbrukeren bør gi en request body som overholder den avtalte kontrakten, fordi dette er det utgivende parten forventer og analyserer for å generere en respons.
I scenario c, ligger problemet hos utgiveren av tjenesten. Responsen er opprettet eller generert av utgiveren av tjenesten og bør alltid overholde den avtalte kontrakten.
I begge tilfeller kan det imidlertid være forvirrende når en feil oppstår med enten forespørselen eller responsen. Siden begge parter bygger programvare og reagerer på hverandre, vil det ikke være klart med en gang hvilken part som har feil. La oss si at en forespørsel blir avvist av API-et fordi request body er feil. Da kan det være at forbrukerparten gjorde en feil ved oppsett av request body, men det kan også være at utgivende part gjorde en feil i den forventede request body. Det samme gjelder for responsen; det kan være en feil fra utgivende part ved å lage en feil respons, men det kan også være en feil fra forbrukerparten ved å tolke den forventede responsen feil. Dette er akkurat hvorfor en kontrakt er så viktig, fordi uten en kontrakt har begge parter like mye si i hva de tenkte eller tolket i form av kode som bør implementeres.
Jeg håper denne bloggen vil hjelpe deg på din QA-reise. I en av mine neste blogger vil jeg fordype meg i hvordan du bruker disse kontraktene i Postman-testene dine, slik at du kan utføre kontraktbasert testing med dette verktøyet!