Krydda testningen med apor och regler

Jag heter Robert Feldt och forskar på programvarukvalitet på Chalmers. Min forskargrupp och jag är intresserade av både teknik, metoder, organisation och processer men gemensamt är att vi tror att man alltid måste fokusera på människorna och hur de påverkas och beter sig. Här på testzonen kommer jag skriva om både högt och lågt, både tekniken och mjukare faktorer. Idag börjar vi med lite teknik.

Slumpmässig testning låter egentligen som en ganska dålig idé. Vi vet ju alla att det kan vara ganska svårt att hitta envetna fel och att vi måste vara ganska kluriga för att hitta ett testfall som verkligen påvisar felet. Varför skulle vi då kunna hitta dessa fel genom att bara slumpmässigt skicka in olika data till den funktion eller det system vi testar? Det är ju nästan befängt, skulle några apor som bara “klickar” på vårt system kunna hitta saker som vi med all vår erfarenhet och kunskap fick spendera en massa tid på att upptäcka.

I huvudsak är det två idéer som gör att slumpmässig testning (random testing) fungerar bättre än vad man först kan tro: det finns många apor och det är lätt att glömma något viktigt. Våra allt snabbare datorer gör helt enkelt att vi idag kan köra en herrans massa slumpmässiga tester på bara några sekunder. Vi kan helt enkelt ersätta vår “hjärn-kraft” med datorkraft. För varje minut mer vi lägger ner på att komma på bra tester kan vi istället köra kanske en miljon fler slumpmässiga tester.

Den andra delen är att även om vi kommer på ett smart testfall som behöver testas så är det lätt att glömma någon annan del av systmet/funktionen. En stor samling apor som slumpmässigt testar vårt system kommer inte glömma något för de har ingen plan från början. Därför är det lika troligt att de klickar på en viss liten del av systemet som en annan medans vi kanske glömmer den.

Ok, säg nu att vi är beredda att ta in slumpen lite mer i vår testning. Vad behöver vi göra? Ja, det räcker inte med att bara sätta upp klassen och anropa den funktion vi testar, vi måste också ha ett sätt att kolla om svaret är rimligt. Ofta brukar detta kallas för orakel med referens till oraklet i Delfi som kunde svara på alla frågor. Problemet är att om vi redan hade ett orakel så skulle vi inte behöva skriva programmet alls!

Men ett stort framsteg i vår kunskap om testning är att vi de senaste åren har kommit på att oraklet inte behöver vara perfekt. Så länge vi har någon slags regel som talar om för oss hur svaret från funktionen skall eller inte skall se ut så kan vi få fram viktig information om vårt system. Vi har alltså insett att reglerna kan vara mer eller mindre perfekta, det finns en skala av hur mycket och hur exakt reglerna stämmer överens med ett orakel som alltid skulle veta precis vad svaret skall vara.

Ett exempel på denna utveckling är så kallad egenskaps-baserad testning (property-based testing). En egenskap är helt enkelt en regel som säger något om svaret. Ett typiskt exempel skulle vara om vi utvecklar en funktion som skall vända på ordningen på värdena i en vektor så vet vi att antalet värden i svaret måste vara samma som i den vektor vi skickar in. Vi vet också att det första värdet i vektorn vi skickar in skall vara det sista värdet i svaret osv. Genom att samla ett antal sådana här egenskaper tillsammans och kombinera dem med slumpmässig testning kan vi komma väldigt långt. Det blir en slags approximation av ett “perfekt” orakel.

Det fina med egenskaperna är att de ofta är enklare att skriva ner än hela vår funktion. De är också betydligt enklare att skriva ner än att lära sig något formellt språk som liknar matematik och som många forskare förespråkat under många år. Genom att gradvis upptäcka och lägga till egenskaper kan vi så att säga skruva upp eller ner volymen på hur detaljerad och exakt vår “specifikation”, och därmed vår testning, av systemet är. Detta ger också möjligheter att vara mer kostnadseffektiv genom att skruva upp volymen på delar som är mer kritiska och skruva ner volymen på mindre viktiga delar.

Förutom regler som fungerar som vårt orakel behöver vi också ett sätt att generera slumpmässiga data av den typ vi vill testa. Detta är ofta ganska enkelt och det finns idag bibliotek som hjälper oss. Men i princip bygger vi upp slumpmässiga mer komplexa data genom att på olika sätt kombinera enkla byggstenar som genererar slumpmässiga heltal, strängar osv.

Jag har gjort ett kodexempel för att visa på de här principerna: http://robert_feldts_code_examples.codepad.org/uV8UP7nm och om du scrollar ner på sidan kan du se resultatet för en körning (programspråket är Ruby men det spelar inte så stor roll, det bör gå att hänga med om du kan några liknande moderna språk). I det här fallet vill vi skriva och testa en funktion som skall vända på elementen i en vektor. Men vi har gjort fel med index när vi traverserar vektorn. Här är felet trivialt och en av egenskaperna vi skrivit (samma antal element, och sista är samma som första) falerar för många de 10 slumpmässigt genererade vektorerna. Jag hoppas ändå du får en känsla för de olika delarna.

Idag finns det mest stöd och erfarenheter från denna typ av testning på ganska låg nivå, för enhetstestning. Men många forskare tittar idag på hur man kan göra det på delsystem och hela system. Vill du ha lite fler pekare så kolla in JCheck (jcheck.org) som integrerar tankarna från det system kallat QuickCheck (http://www.cse.chalmers.se/~rjmh/QuickCheck/) som några kollegor till mig här på Chalmers (Koen Claessen och John Hughes) tog fram för några år sedan och som först presenterade de här idéerna. QuickCheck har bla använts av Ericsson för att testa implementeringen av kommunikationsprotokoll.

Det var allt från mig idag, jag hoppas vi “hörs” igen här på TestZonen. Du kan hitta mer från mig via Twitter ( http://twitter.com/#!/drfeldt ) eller på min hemsida ( http://www.cse.chalmers.se/~feldt ).

Må gott!

About Robert Feldt

Robert Feldt
Forskare Chalmers

Robert forskar på Chalmers inom Testning, V&V och Programvaruteknik. Han har utvecklat programvara sedan han var 7 år gammal och har jobbat professionellt sedan han var 15. Han brinner för att göra världen till en bättre plats med programvara!