Skalbar testautomatisering i 3D

2015-03-17 Artikelbanken Test Läst 4807 gånger
”Men kommer det att skala?” kunde man fråga för att låta smart på möten enligt en skämtsam artikel jag läste nyligen. Det kan vara lätt att avfärda detta som en klyscha, men om du har påbörjat en automatiseringssatsning med avsikt att stötta upp din regressionstestning är frågan i högsta grad relevant. Skillnaden mellan att få några få tester att köra mot att skapa en stor testsvit som ständigt levererar värdefull återkoppling är stor. Denna artikel fokuserar på hur du kan tänka runt själva testerna och de tre dimensioner de verkar i: höjd, bredd och tid, det vill säga på vilken nivå ska de ligga; hur många ska du ha och när ska de köras? Kan du svara på dessa frågor kan du också känna dig mer trygg med att kunna svara på frågan om det verkligen kommer att skala.
Det finns goda skäl för att kalla ”automatiserade tester” för ”automatiserade checkar”. Sympatiserar du redan med denna skiftning i språket kan du med fördel byta ut ”test” mot ”check” i artikeln. Är du intresserad av läsa mer om denna språkliga distinktion så rekommenderas denna länk:
http://www.satisfice.com/blog/archives/856

Automatisering utan begränsningar

Om vi föreställer oss en automatiseringslösning där vi kan bortse från verkligheten, vilka egenskaper skulle den då ha? Gissningsvis kommer de flesta att önska sig något i stil med detta:

  • Robusthet – Testfallen går inte fel så länge beteendet i den testade koden inte ändras.
  • Underhållbarhet – Testfallen behöver sällan uppdateras och när det väl händer så görs det lätt med några få kodändringar.
  • Exekveringstid – Alla tester stökas av på några få millisekunder så att direkt återkoppling kan fås om något gick sönder i de senaste ändringarna av systemet.
  • Testtäckning – Hela systemet motioneras och vi får verifierat att alla delar samverkar som det är tänkt.
  • Spårbarhet – När ett testfall plötsligt slutar fungera kan vi direkt se vilken kodrad som orsakade problemet.

I verkligheten går allt detta förstås inte att uppnå. Vissa saker står mer eller mindre i motsatsförhållande till varandra, medan några av de andra skulle kosta alldeles för mycket att realisera för att det någonsin skulle vara värt investeringen. För att förstå vilka kompromisser som behöver göras och för att hitta en vettig balans bland dina tester är det nu dags att dyka in i de tre dimensionerna.

De tre dimensionerna

Höjd

Höjden är helt enkelt vilken testnivå dina tester befinner sig på. Desto högre testnivå desto större del av systemet kommer att testas ihop.

Längst ned i botten har vi enhetstester som testar enskilda funktioner i koden, och på högsta nivån har vi tester som inbegriper alla delar av systemet, det vill säga tester som arbetar mot grafiska gränssnitt och ibland också över flera noder i distribuerade system.  Däremellan har vi flera nivåer av tester, i mitt exempel har jag tagit med integrationstest och systemtest. Det finns dock många varianter på denna bild med olika antal nivåer och med olika namngivning. Tester på hög nivå har typiskt dessa egenskaper:

  • Svarar på frågor så som: ”Fungerar systemet ihop som helhet?".
  • Mycket kod motioneras i varje testfall.
  • Kräver ofta mycket underhåll då ändringar på alla nivåer kan tänkas påverka systemets beteende.
  • Är ofta mindre robusta då fler saker i kedjan kan störa testet.
    Några exempel: För ett distribuerat system kan avbrott i nätverkstrafiken ge oönskade effekter; för GUI-tester kan popup-fönster från operativsystemet störa och för större IT-system finns det stora utmaningar i att ha testdata som är inbördes konsekvent och förutsägbart.
  • Lång exekverings-, uppsättnings- och uppstädningstid. Nätverkssessioner ska sättas upp, webbläsare ska startas, databaskopplingar ska etableras; många är sakerna som kan medföra att testfallet inte kan exekveras så snabbt som man skulle önska.
  • Svårt att spåra fel. När ett testfall går fel på hög nivå kan defekten potentiellt finnas på vilken som helst av miljontals rader av kod (eller i testmiljön som nämnt i punkten innan). Det är därför relativt kostsamt att utreda varför ett testfall gick fel.

På motsvarande sätt har tester på låg nivå typiskt dessa egenskaper:

  • Lätt att spåra felorsak. Då ett testfall går fel bör felet finnas bland ett litet antal kodrader och är för det mesta lätt att hitta.
  • Hög robusthet. Få beroenden till omgivande faktorer och mindre chans att något ändrats gör att testfall på låg nivå ofta är väldigt robusta.
  • Snabba att exekvera. Testfallens exekveringstid beror i stort sätt bara på hur kraftfull dator de exekveras på. Väntetider för att få svar från externa system eller att starta upp tunga applikationer förekommer inte här.
  • Täcker inte upp för hur olika delar av koden samverkar. Det kan finnas tusentals bra lågnivåtester som alla går igenom utan problem, men detta är i sig ingen garanti för att alla delar kommer att fungera ihop, bara att varje enskild del kan bete sig som förväntat.
  • Varje test täcker bara en väldigt liten del av koden.

Bredd

Ju fler tester du har, desto större är din testbredd. Att öka testbredden kan antingen handla om att variera indatat till en funktion på många olika sätt, eller att täcka upp fler funktioner och fler felfall. Att skapa många tester på detta sätt ger en högre testtäckning men ökar också på den totala testmängden. Baksidan med detta är att du kommer att ha fler tester att underhålla och att det kommer att ta längre tid att köra igenom alla. Det är ofta väldigt lätt att lägga till fler liknande tester när det första testet är skapat, men det är betydligt mer kostsamt att långt senare försöka städa upp bland alla tester när antalet växt sig för stort. Att införa lösningar som datadriven testning kan underlätta underhållet men det bibehåller problemet med en ökande exekveringstid.

Tid

Eftersom automatisering ofta handlar om att få snabb återkoppling är tiden en kritisk faktor att ta hänsyn till. Hur ofta vill du att testerna körs? Hur lång tid får de ta i så fall? Vill du till exempel att en viss svit av tester ska köras igenom för varje ändring som görs av en utvecklare innan de checkar in sin kod får inte exekveringstiden bli för lång, eftersom det då blir ohållbart. Tester som tar lång tid att köra kanske bara är möjliga att köra på natten eller till och med över helgerna, vilket förstås förlänger återkopplingstiden avsevärt. Det är självklart, i vissa fall, möjligt att parallellisera testerna men detta kräver en mer komplex automatiseringslösning. Dessutom fungerar det bara om testerna kan köras oberoende av varandra eller i separata miljöer. Om inte vettiga avvägningar görs i tidsdimensionen riskerar din automatiseringslösning att bli en irriterande bromskloss i utvecklingsflödet.

Hitta balansen

Hur kan man då hitta en balans mellan de tre dimensionerna så att automatiseringen blir hållbar i längden? Det finns förstås ingen skräddarsydd allt-i-ett-lösning för att hitta denna balans, utan du måste utgående från din verklighet hitta den för att få ut det mesta möjliga av dina automatiserade tester. Några grundläggande principer kan vara värda att ta fasta på dock.

Brett i botten – smalt på toppen

Topptung automatisering har samma egenskaper som en topptung byggnad: den tippar lätt. Om majoriteten av dina tester ligger på hög nivå riskerar du att hamna i en situation där du tillbringar nästan all tid med att felsöka, laga och följa upp tester som fallerat av okänd orsak. Din testsvit kommer dessutom snabbt växa i exekveringstid. Förtroendet för hela din automatiseringssatsning riskerar att försvinna fortare än kvickt då ingen kan se vilket värde den bidrar med. En etablerad modell i automatiseringsvärlden är den så kallade testautomatiseringspyramiden. Den finns i en mängd varianter med olika namn på de olika nivåerna. Vilken namngivning som används är dock inte så viktigt. Det viktiga är principen att absolut flest tester bör finnas på den lägsta nivån medan toppnivån innehåller relativt få tester.På så sätt erhålls en bred bas och en smal topp och automatiseringen blir lika stabil som en välbyggd pyramid. Vissa sticker ut hakan och anger hur många procent av testerna som bör ligga på respektive nivå, men riktigt så långt vill inte jag gå då varje sammanhang är unikt. Däremot anser jag att principen ”ju högre nivå desto färre tester” ofta är en rimlig ansats. Använd lågnivåtester för att motionera alla möjliga och omöjliga varianter av en funktionalitet och satsa på några få enkla scenarion på hög nivå för att få återkoppling om hela systemets grundfunktionalitet påverkats. Förutom robusthetsaspekten ökar då dessutom chansen att fånga fel på så låg nivå som möjligt, vilket både kortar återkopplingstiden och underlättar felsökningen.

Bygg underifrån

Inom produktutveckling börjar man oftast med en enkel prototyp och lägger sedan över tid till fler och fler funktioner. På samma sätt är det klokt att angripa testautomatisering. Börja enkelt på låg nivå och få det att fungera. När du känner dig komfortabel med den nuvarande nivån kan du försöka dig på med komplexa tester på en högre nivå. På så sätt ökar du dina chanser för att lyckas i varje steg, dels för att det är enklare att automatisera på en lägre nivå, och dels för att du skaffar dig nyttiga erfarenheter längs med vägen. Skulle du ändå misslyckas kan du ”trösta” dig med att du med största sannolikhet inte skulle ha lyckats med din satsning på en högre nivå heller. Dessutom har du då förmodligen bara investerat en bråkdel av kostnaden i lågnivåtester gentemot vad det skulle ha kostat med dyra verktyg, miljöer etc. för att få högnivåtester att fungera.

Och kom ihåg! Testautomatiseringspyramiden är bara en riktlinje. Bara för att den har en topp behöver det inte betyda att du måsta automatisera på alla nivåer. Ifrågasätt nyttan med att automatisera på en högre nivå; vad tillför det jämtemot de tester som redan finns på nuvarande nivå och de tester som körs manuellt på den högre nivån? Många drar sig till exempel med rätta för att försöka automatisera på GUI-nivå då historien av full av misslyckanden för sådana projekt. En pyramid utan topp är fortfarande en väldigt stabil konstruktion, även om man inte ser lika långt högst upp.

Portionera och städa

För att hitta balansen i tidsdimensionen kan en bra grundansats vara att dela upp dina tester i olika sviter som kan köras olika ofta. En stor mängd enhetstester kan köras igenom på några sekunder eller i värsta fall minuter och det finns sällan någon anledning att inte köra dessa vid varje kodändring - snabb återkoppling är värd oerhört mycket. När det kommer till tester på högre nivå, som vi konstaterat ofta tar tid att köra och underhålla, kan det vara riskabelt att kräva att alla dessa måste köras i vissa utvecklingssteg,  exempelvis innan incheckning av kod. De riskerar helt enkelt att bromsa utvecklingsarbetet för mycket. Några få utvalda högnivåtester för att verifiera att systemet fortfarande kan ”lira ihop” kan dock med fördel köras ofta. Förbered dig också på att kontinuerligt behöva portionera om och rensa i testsviterna när antalet tester ökar och exekveringstiden börjar bli för lång. Att plocka bort fungerande tester ur en svit kan kännas läskigt, men är ibland nödvändigt ont. Just osäkerheten med att plocka bort gamla tester talar för att det i många fall är klokare att tänka till när testerna skapas. Insikten att det finns en kostnad med varje tillagt test är därför viktig att hela tiden ha i bakhuvudet och att kommunicera ut.

Sammanfattning

Att bygga en skalbar testautomatiseringssatsning är en stor utmaning. Fallgroparna är många och risken att det kommer att kosta mer än det smakar är stor. Genom att redan från början beakta de tre dimensionerna höjd, bredd och tid i din strategi ökar du chanserna att bygga något robust som inte är för topptungt och samtidigt levererar frekvent och värdefull återkoppling på dina kodändringar.

Nästa steg

Det finns många fler aspekter för att lyckas med testautomatisering än de denna artikel riktat in sig på. Du måste förstå vad syftet med din automatisering är, anpassa den till ditt sammanhang, välja rätt verktyg, få gehör i organisationen, etc. Läs gärna mer i någon av dessa artiklar:

KONTAKTA OSS

Har du frågor? Vill du ha hjälp med områden inom kravhantering och test?
Hör av dig till oss! Vi hjälper dig gärna. 

Kontakt 

Dela artikeln