Alv Logo
Del

5 tips til clean code i systemutviklingstester

I min forrige artikkel om tester skrev jeg litt om de grunnleggende prinsippene for å få mest mulig verdi ut av testene sine.

"5 prinsipper for bedre tester i systemutvikling" kan du lese her.

I denne artikkelen vil jeg fokusere litt mer på det praktiske rundt clean code og hvordan å gjøre testene dine mest mulig leselige og oversiktlige. En ting er å følge prinsippene beskrevet i den forrige artikkelen min for å skrive tester som dekker flest mulig brukstilfeller og gir så mye nytte som mulig, men det er vel så viktig å skrive tester som er lett forståelige, lette å lese og dermed lette å vedlikeholde. En annen fordel med å skrive leselige tester er at de kan brukes som dokumentasjon til koden. Dersom man lurer på hva en klasse gjør er det fint å kunne gå til testene til klassen og få et klart bilde derfra basert på navnene og funksjonene til testene.

En annen fordel med å skrive leselige tester er at de kan brukes som dokumentasjon til koden.

1. Navngiving

Så hvordan skriver man «pene» tester? Det første å tenke på er ofte navngiving. Her foretrekker jeg å bruke en navnestandard inkluderer følgende informasjon: metode som testes, hvilket scenario som testes og hva som er forventet resultat. Da vil et navn på en test se for eksempel slik ut: Calculator_AddTwoPlusTwo_ReturnsFour. På denne måten er det veldig raskt å skjønne hva testens formål er uten å måtte sette seg inn i masse kode. Dette gjør også at en liste over feilende tester lett kan gi deg en indikator på at koden nå ikke lenger støtter partall dersom alle dine partalltester feiler. Dette kan ta lang tid å finne ut av dersom man må lese gjennom alle feilende tester etter en omskriving.

2. Oppsett

(...) med mer kompliserte tester er det veldig nyttig å dele opp i logiske deler.

Når man har gitt navn til testen kan man tenke på hvordan å strukturere selve testen. En kjent stil å sette opp testene sine er Arrange, Act, Assert. Det vil si at første del av testene bør bestå av kode som setter opp betingelsene for testene, deretter utfører den nødvendige logiske operasjoner og til slutt sjekker om resultatet fra operasjonene gikk som forventet. Sammenlign for eksempel de to kodebitene nedenfor og se hvilken du syns er enklest å lese. Dette er selvfølgelig et trivielt eksempel, men med mer kompliserte tester er det veldig nyttig å dele opp i logiske deler.

3. Hold det enkelt

Neste tips er å holde input til testen så enkel som mulig. Dersom testene kun skal sjekke formateringen på en string, ikke overkompliser det ved å introdusere lesing fra tekstfil eller gjøre masse unødvendige operasjoner på stringen. La testen ta inn minimum av nødvendig data for å gjøre det den skal. På den måten blir de mer stabile mot endringer og mye mer leselige.

Begynner man å gjøre mange slik operasjoner i tester er det stor sjanse for å introdusere bugs (...)

Mens vi snakker om å gjøre unødvendige operasjoner i tester, logiske operasjoner som if, while, for osv. bør holdes langt unna tester. Dette er kode som selve metoden som testes eventuelt bør inneholde. Begynner man å gjøre mange slik operasjoner i tester er det stor sjanse for å introdusere bugs og gjøre seg sårbar mot senere endringer i koden. En god regel er at tester skal fokusere på sluttresultat, ikke implementasjon.

4. Ikke gjenbruk objekter

Dette punktet er noe relatert til I-en i FIRST-prinsippene, altså «isolated». Dersom testene dine er avhengige av et eller annet objekt eller state, lag dem heller i en hjelpemetode enn å bruke Setup og Teardown. Da blir all kode synlig i hver test, slik at man får det fulle bildet uten å måtte lese flere metoder samtidig som at tester ikke deler på objekter som kan påvirke resultatet.

5. Hold deg til ett scenario per test

En test bør aller helst teste kun en ting av gangen. Den kan godt teste flere ting innenfor samme scenario, men alle sjekkene den gjør bør være relatert til den samme operasjonen. Et eksempel kan være en test av et endepunkt. Her vil det være naturlig å teste at ved en vellykket operasjon skal endepunktet returnere status 200, og et resultat i en eller annen form. Det som derimot ikke er ønskelig er at man tester det samme endepunktet med to forskjellige inputs i samme testen, hvor ett av inputene forventes å feile mens det andre forventes å være ok.

Med en gang vi må lage et mer generisk navn til testen tyder det på at den biter over for mye.

Ta for eksempel testene nedenfor. Den første testen tester to ulike ting, men begge tingene er innenfor samme scenario, dvs det er bare én sti som testes. I test nummer to derimot testes to ulike stier i samme test, både en sti som er ok og en sti som feiler. Dette fører til mer rot og lager bare utfordringer i debuggingen. I tillegg ser vi at navnet på testen, GetPeople_LastNameStartsWithA_10People ikke lenger stemmer. Med en gang vi må lage et mer generisk navn til testen tyder det på at den biter over for mye.

En ulempe som er verdt å nevne med å ha flere asserts i en test er at dersom den første asserten feiler får man ikke resultatet fra den andre og må først rydde opp i den første feilen før man eventuelt finner videre feil. Her det det en fin balansegang mellom å teste nok ting og beholde leselighet og enkelhet. Ett spesifikt tilfelle der det kan lønne seg å ha flere asserts er dersom man vil sjekke at en operasjon var vellykket OG faktisk returnerte data. Et endepunkt kan for eksempel returnere en statuskode 200 som indikerer at alt gikk bra, men det betyr ikke nødvendigvis at det kommer noe data ut av endepunktet. Da kan det være kjekt å ha én assert som sjekker statuskoden i tillegg til en assert som sjekker at endepunktet returnerte noe av verdi.

Konklusjon

Dette er et tema det er mye å si om og denne artikkelen skraper litt i overflaten av clean code i testskriving og gir forhåpentligvis et raskt overblikk over noen av de viktigste prinsippene. I tillegg til disse tipsene gjelder selvfølgelig de fleste andre clean code prinsipper som ikke er test-spesifikke, som for eksempel å samle like funksjoner i én klasse, gi gode variabelnavn og alle de kjente og kjære akronymene som KISS, DRY, YAGNI og så videre.

Del
Les også
Les mer

5 prinsipper for bedre tester i systemutvikling

I et systemutviklingsprosjekt kan fort tester være noe som blir nedprioritert. Det tar tid å skrive gode tester, og man ser ikke umiddelbart verdien av arbeidet. Dette gjør at det...

5 prinsipper for bedre tester i systemutvikling

Systemutvikling
Pål Bøckmann - 4/20/2021
Les mer

Er mikrotjenester alltid bedre enn monolitter?

Mikrotjenester er noe man har hørt mye om på konferanser og forum i en stund nå, men sett bort ifra den tekniske løsningen; Hvordan passer dette egentlig inn i et...

Er mikrotjenester alltid bedre enn monolitter?

Systemutvikling
Pål Bøckmann - 10/15/2020
Les mer

Å starte på bar bakke

Pål er utdannet sivilingeniør i Kjemi fra NTNU i Trondheim. Kjemi er ikke akkurat A4-utdannelsen for en systemutvikler. I denne bloggposten forteller derfor Pål om sin vei inn mot systemutviklingsdomenet...

Å starte på bar bakke

Systemutvikling
Pål Bøckmann - 2/19/2020