Vad betyder 84% kodtäckning?

Inledning

I min tidigare artikel ”Syftet med testningen – är vi säkra på att vi förstår?” så diskuterades olika alternativa syften med testning. Det konstaterades att ett av de mer framträdande syftena är att mäta kvaliteten, både på produkten och på själva testarbetet – allt för att kunna ta så informerade beslut som möjligt. När det gäller kvaliteten på testarbetet används ofta olika former av täckningsmått som bas för mätningarna. I denna artikel tittar jag närmare på en del av dessa täckninsmått för att försöka förstå vad man egentligen kan använda dessa mått till.

Täckningsmått

Som namnet antyder, utgår alla täckningsmått som används inom testning ifrån att man vill ”täcka” någon aspekt av testobjektet med tester/testfall. Gemensamt för alla täckningmått är att man utgår ifrån någon typ av uppräkningsbara ”element”. Uppräkningsbart i detta sammanhang betyder att det finns ett ändligt antal element av den typ man är intresserad av. Exempel på element som kan användas för att basera testrelaterade täckningsmått är bland annat kodsater (kodrader), krav och risker.

Alla täckningsmått mäts i procent och beskriver hur stor andel av de aktuella elementen som hittills täckts av den testning som genomförts (eller förberetts – i fallet med testfallsframtagning med avseende på kravtäckning).

De flesta typer av täckning är relativt lätta att mäta och dessutom ganska lätta att förstå, vilket gör dem populära att använda. Denna enkelhet kan dock vara missledande och ge upphov till grava felbeslut om man inte till fullo förstår vad ett uppmätt täckningsresultat egentligen betyder. Redan Boris Beizer konstaterade att om man uppnått 85% täckning, hur vet man då att det är rätt 85%…?

Kravtäckning

Kravtäckning handlar om hur stor del av kravmassan som täckts av testfall. Man kan använda kravtäckningsmått både under testfallsframtagningen och under testexekveringen. Täckningen kan ju handla antingen om hur stor del av kravmassan vi har testfall för eller hur stor del av kravmassan vi verkligen har testat.

Kravtäckning har minst tre generella svagheter: (1) kravkvalitetsberoenden; (2) subjektivitet  och (3) mätproblematik.

Den första svagheten, kravkvalitetsberoenden, har att göra med att kravtäckning bygger på de existerade kraven, utan hänsyn till om dessa krav utgör en komplett kravmassa eller inte. Det är (relativt) enkelt att uppnå hög täckning av en liten mängd krav, men om det finns ett antal ickeformulerade (underförstådda) krav eller om det helt enkelt saknas krav så kommer den höga kravtäckningsmätningen att ge en falsk säkerhet.

Den andra svagheten, subjektivitet, relaterar dels till att ett krav kan formuleras på många olika sätt och dels till att många gånger inte är uppenbart vilket eller vilka testfall som ska användas för att kravet ska anses vara täckt. Vad är det som säger att ett visst (eller en viss uppsättning) testfall täcker ett krav? Här är det oftast den individuella testaren, eller en granskningsgrupp som avgör, vilket introducerar en subjektivitet i täckningsmåttet.  Det finns med andra ord en risk att olika personer, med samma uppsättning krav och testfall, kommer till olika slutsatser om kravtäckningen.

Den tredje svagheten, mätproblematik, är delvis relaterad till subjektivitetsproblemet ovan. För att täcka ett krav krävs många gånger mer än ett testfall. På motsvarande sätt kan samma testfall täcka delar av flera krav. Det finns med andra ord en många-till-många relation mellan krav och testfall. Dessa komplexa relationer gör att man kan få problem vid kravtäckningsanalyser, i synnerhet vid testexekveringen. Hur räknar man täckningen för ett krav där bara en delmängd av de relaterade testfallen har exekverats? En viss täckning har ju uppnåtts men kravet är ju inte helt täckt.

Strukturbaserad täckning

Strukturbaserad täckning handlar om att täcka vissa strukturelement med testfall. Den vanligaste formen av strukturbaserad täckning är kodtäckning, i vilken någon form av kodelement (kodrader, beslutspunkter, variabler mm) används för att formulera täckningsmått. Strukturbaserad täckning kan även mätas på högre abstraktionsnivåer, till exempel baserat på funktionsanrop, klasser, metoder eller gränssnittsobjekt. Oavsett nivå så utvärderas alltid den strukturbaserade täckningen under själva testexekveringen.

Även den strukturbaserade täckningen har flera urskiljbara svagheter: (1) många täckningsmått; (2) inexakta definitioner av flera täckningsmått; (3) hög kostnad och (4) kort ”bäst-före-datum”.

Vid seriösa försök att mäta kodtäckning krävs verktygsstöd – det är helt enkelt inte möjligt att för hand hålla ordning på vilka delar av koden som täcks av en viss grupp testfall. Som beskrivits ovan baseras kodbaserad täckning på olika former av kodelement. Ett problem i detta sammanhang är att det finns en stor mängd olika typer av kodelement, vilket ger upphov till en ännu större mängd kodbaserade täckningsmått. Eftersom det är ganska enkelt att automatisera kodtäckningsmätningar, stödjer de verktyg som finns tillgängliga ofta en stor mängd olika täckningsmått. Vilket leder till den första svagheten med kodtäckning, d.v.s., den otydlighet som det innebär att inte definiera exakt vilket mått man avser.

Den andra svagheten med kodbaserade täckningsmått är att flera av de vanliga måtten kan tolkas på flera olika sätt. Ta beslutstäckning som exempel. Beslutstäckning bygger på att man med testning täcker ”alla utgångar  från varje besult”. Ett problem med beslutstäckning handlar om switch-satser. En switch-sats (även kallad case-sats i vissa programmeringsspråk) har som bekant flera utgångar. En tolkning av beslutstäckning bygger på att man direkt utgår ifrån själva switch-satsen, medan en annan är att man först transformerar switch-satsen till ett antal nästlade if-satser. Som framgår av figur 1 leder dessa tolkningar till totalt sett olika många utgångar som måste täckas. Liknande problem går att identifiera för många av de kodbaserade täckningsmåtten.


Figur 1. En switch-sats som leder till tre utgångar ger en hierarki av if-satser med totalt fyra utgångar.

Konsekvensen av detta är att samma täckningsmätningar gjorda med olika vertyg på  samma testobjekt med samma uppsättning testfall  kan ge olika resultat.

En tredje svaghet med kodbaserad täckning är den relativt höga kostnaden för att skapa varje testfall. Antag att målet med ett visst testfall är att täcka kodrad nummer 132 (som hittills inte testats). Testaren behöver då göra en bakåtanalys av koden, från denna punkt för att ta reda på vilka värden som ska utgöra indata för testfallet för att exekveringen ska nå den önskade punkten. Denna analys, kan vara mer eller mindre komplicerad, och därmed dyr, beroende på kodens utformning. Dessutom kan man inte göra denna analys innan koden är tillgänglig, d.v.s. testfallsframtagningen kommer att konsumera värdefull testexekveringstid. Vidare händer det att man vid programmeringen med flit använder ”omöjliga” kodkonstruktioner. Till exempel kan man skapa en oändlig loop genom att att skriva ”WHILE (true)”. Detta är kanske vad man vill uppnå, men det leder till att denna beslutspunkt aldrig kan bli falsk, vilket i sin tur gör det omöjligt att nå 100% beslutstäckning.

En fjärde viktig svaghet med kodbaserad täckning är att den uppmätta täckningen har ett kort ”bäst-före-datum”. Det vill säga, så fort kodens struktur (även om det bara handlar om att lägga till en enda print-sats) så riskerar den tidigare uppmätta täckningen att invalideras och man måste mäta om genom att exekvera om ALLA testfall.

Risktäckning

Riskbaserad täckning bygger på att man först genomför en teknisk (produkt) riskanalys . Därefter täcker man alla eller de mest viktiga riskerna med testfall för att säkerställa att de identifierade riskerna inte finns eller åtminstone inte leder till så allvarliga konsekvenser som man först trott.

Svagheterna hos risktäckning påminner om svagheterna med kravtäckning : (1) kvaliteten i riskanalysen; (2) subjektivitet  och (3) mätproblematik.

Om riskanalysen inte gjorts tillräckligt bra kommer en hög täckning att ge en falsk säkerhet. Ej identifierade, men i högsta grad konkreta risker, står otestade. En relaterad svaghet är att både riskidentifieringen och riskvärderingen delvis bygger på subjektivitet, vilket innebär att ”fel” risker kan komma att ingå i den mängd risker som utgör basen för testtäckningen.

Slutligen finns här en likartad mätproblematik som vid kravtäckning. Hur vet man att en viss mängd testfall verkligen ”täcker” er risk och hur redovisar man en situation när bara vissa testfall som hör till en risk har exekverats?

Täckningsmått – varför ens bry sig?

Med alla dessa svagheter över hela linjen, är det mödan värt att ens försöka mäta täckningen? Min åsikt är tveklöst JA, men vi måste använda täckningsinformationen på ett intelligent sätt.

Ofta förespråkas att man använder definierade nivåer av täckning i sina avslutskriterier. Men med de inbyggda osäkerheterna i de olika täckningsmåtten är detta egentligen bara intressant om man har för avsikt att nå 100% för en viss typ av testning. Vid lägre målnivåer finns alltid Boris Beizers argument – hur vet man att det är rätt delmängd? Även vid en målformulering om 100%,och om man uppnått detta, så är detta i realiteten inte särskillt upplysande. Det enda vi vet vid 100% täckning är att testningen med avseende på just den sortens täckning inte kommer att ge högre täckning.

Däremot är täckningsmätningar av alla slag mycket värdefulla för att peka ut områden av produkten som ännu inte har testats [tillräckligt]. Allt som inte testats är potentiella risker och täckningsmätningar är ett av de bästa sätten att identifiera dessa risker. I detta sammanhang är det troligt att inte ”allt” är lika viktigt att täcka, och det kan till och med vara rätt strunta i att testa vissa saker. Därför behöver täckningsmätningar kompletteras med analyser av hur viktigt det är att testa vissa områden.

Ett annat intressant användningsområde för täckningsmått är att mäta testprogress över tiden. Till exempel kan man i en graf visa hur en viss typ av täckning ökar över tiden. Detta kan ge tidiga indikationer på om testteamet har tillräckligt hög progress för att man ska nå de uppsatta målen. Det kan på ytan se ut som om man använder täckning för att formulera avslutningskriterier, men tanken är här att titta på progress snarare än slutmål.

En konsekvens av de svagheter och osäkerheter som finns i täckningsmätningar är att det knappast är något ide att jobba med enskillda procent vid täckningsmätningen, framförallt vid redovisning av hittills uppnådd täckning.

Min slutsats är alltså att täckningsmätningar är viktiga att genomföra, men dessa ska i första hand användas som bas vid beslut om var vidare testning ska genomföras och vid analyser om testprogressen är tillräckligt hög. Enbart i andra hand bör täckningsmätningar ligga till grund för progressredovisning och beslut om när testningen ska avslutas.

About Mats Grindal

Mats Grindal
AddQ

Mats Grindal har arbetat som konsult i testbranschen i närmare 20 år. Under tiden som konsult har han haft en lång rad uppdrag som spänner från testare, via testledare till test strateg, mentor och lärare. Han har även hunnit med att avlägga en doktorsexamen inom testning av mjukvara vid Linköpings Universitet.