Vad är mutationstestning? (Exempel)
⚡ Smart sammanfattning
Mutationstestning introducerar avsiktligt små fel i källkoden och kör sedan den befintliga testsviten mot varje felaktig version, för att mäta om dessa tester är tillräckligt starka för att upptäcka förändringen.
Vad är mutationstestning?
Mutationstestning är en typ av mjukvarutestning där vissa uttalanden i källkoden ändras, eller muteras, för att kontrollera om testfallen kan hitta fel i källkoden. Målet med mutationstestning är att säkerställa testfallens kvalitet vad gäller robusthet, så att de misslyckas mot den muterade källkoden.
Förändringen som görs i ett mutantprogram måste hållas extremt liten, så att den inte påverkar programmets övergripande mål. Mutationstestning kallas också en felbaserad teststrategi, eftersom den innebär att man avsiktligt skapar ett fel i programmet. Det är en form av Vit Box Testning som huvudsakligen tillämpas under Enhetstestning.
Mutationstestning föreslogs 1971 i en studentuppsats av Richard Lipton och formaliserades i artikeln "Hints on Test Data Selection" från 1978 av DeMillo, Lipton och Sayward. Den tappade fart på grund av den tidens datorkraft och har sedan dess återvunnit mark för språk som Java, C#, Python, JavaSkript och XML.
Hur utför man mutationstestning?
Följande är stegen för att utföra mutationstestning, även känd som mutationsanalys:
Steg 1: Fel introduceras i programmets källkod genom att skapa många versioner som kallas mutanter. Varje mutant ska innehålla ett enda fel, och målet är att orsaka att mutantversionen misslyckas, vilket visar testfallens effektivitet.
Steg 2: Testfall tillämpas på det ursprungliga programmet och även på mutantprogrammet. Testfall bör vara adekvat, och det är justerat för att upptäcka fel i ett program.
Steg 3: Jämför resultaten från originalprogrammet och mutantprogrammet.
Steg 4: Om det ursprungliga programmet och mutantprogrammet genererar olika utdata, så dödas mutanten av testfallet. Därför är testfallet tillräckligt bra för att detektera skillnaden mellan det ursprungliga och mutantprogrammet.
Steg 5: Om det ursprungliga programmet och mutantprogrammet genererar samma utdata, hålls mutanten vid liv. I sådana fall behöver effektivare testfall skapas som dödar alla mutanter.
Diagrammet nedan tracgenomgår samma fem steg, från det ursprungliga programmet via mutantgenerering till den dödade eller överlevande domen.
Hur skapar man mutantprogram?
En mutation är inget annat än en enda syntaktisk ändring som görs i en programsats. Varje mutantprogram ska skilja sig från det ursprungliga programmet med exakt en mutation.
| Originalprogram | Mutant program |
| Om (x>y) Skriv ut "Hej" annars Skriv ut "Hej" |
Om (x Skriv ut "Hej" annars Skriv ut "Hej" |
I paret ovan har endast jämförelseoperatorn ändrats, men ett testfall där x är större än y skriver nu ut "Hej" istället för "Hej". Illustrationen visar den syntaktiska redigeringen.
Vad ska man ändra i ett mutantprogram?
Det finns flera tekniker som kan användas för att generera mutantprogram. De tre familjerna nedan täcker de flesta mutationsoperatorer som verktyg levereras med.
| Operand ersättningsoperatörer | Operatorer för uttrycksmodifiering | Operatorer för modifiering av uttalanden |
| Ersätt operanden med en annan operand (x med y, eller y med x) eller med ett konstant värde. | Ersätt en operator, eller infoga en ny operator, i en programsats. | Programmatiska uttalanden modifieras för att skapa mutantprogram. |
| Exempel: If(x>y) ersätt x- och y-värden If(5>y) ersätt x med konstant 5 |
Exempel: Om(x==y) Vi kan ersätta == med >= och få mutantprogrammet som If(x>=y) och infoga ++ i satsen Om(x==++y) |
Exempel: Ta bort den andra delen i en if-else-sats Ta bort hela if-else-satsen för att kontrollera hur programmet beter sig |
Några exempel på mutationsoperatorer:
- GOTO etikettbyte
- Returnera uttalande ersättning
- Utlåtande radering
- Insättning av unär operator (t.ex. – och ++)
- Logisk kontaktbyte
- Jämförbart ersättning av arraynamn
- Ta bort else-delen av en if-else-sats
- Lägga till eller ersätta operatorer
- Uttalandet ersätts genom att ändra data
- Datamodifiering för variablerna
- Ändring av datatyper i programmet
Operatorer som berör ett randvillkor överlever oftast, så mutationsresultat pekar ofta tillbaka på luckor i gränsvärdesanalys.
Typer av mutationstestning
In MjukvaruutvecklingMutationstestning delas i grunden in i tre typer – påståendemutation, värdemutation och beslutsmutation.
- Uttalande mutation – en sats klipps ut, klistras in eller raderas, så resultatet kan bli att några kodrader tas bort.
- Värdemutation – värdena för primära parametrar och konstanter modifieras, till exempel genom att ändra en loopgräns eller ett tröskelvärde.
- Beslutsmutation – kontrollsatser ändras, till exempel flipping en relationsoperator eller negerar ett villkor.
Verktyg grupperar sina operatorer under dessa tre rubriker, så familjen som producerade en överlevande mutant berättar för testaren vilken typ av påstående som saknas. En överlevande beslutsmutant markerar vanligtvis en otestad gren, som överlappar med looptestning.
Automatisering av mutationstestning
Mutationstestning är extremt tidskrävande och komplicerat att utföra manuellt, så det är lämpligt att använda automatiseringsverktyg, vilket också minskar kostnaderna. Ett mutationsverktyg sammanställer mutanterna, schemalägger körningarna, registrerar vilken mutant varje misslyckad test dödade och rapporterar poängen.
Lista över tillgängliga verktyg:
- Stryker — ett ramverk för öppen källkod för mutationstestning med utgåvor för JavaManus och TypeScript (StrykerJS), C# och .NET (Stryker.NET) och Scala (Stryker4s).
- GROP, även skrivet PITest — ett mutationstestningssystem för Java och JVM:n som muterar kompilerad bytekod och ansluts till Maven och Gradle bygger bredvid JUnit.
Båda körs som ett byggsteg, så de hör hemma i samma kontinuerlig integration rörledningen som resten av automatiseringstestning på.
Mutationspoäng
Mutationspoängen definieras som andelen dödade mutanter av det totala antalet mutanter.
Mutationspoäng = (dödade mutanter / totalt antal mutanter) * 100
Formeln visas nedan i den form som de flesta verktyg rapporterar den.
Testfall beskrivs som mutationsadekvata när poängen når 100 procent. I praktiken måste nämnaren exkludera ekvivalenta mutanter — mutanter vars ändrade syntax beter sig exakt som originalet, så inget test kan döda dem. Verktyg rapporterar därför dödade mutanter dividerat med dödade plus överlevande icke-ekvivalenta mutanter, och låter testaren flagga ekvivalenter.
Experimentella resultat har visat att mutationstestning är ett effektivt sätt att mäta testfalls lämplighet. Den största nackdelen är kostnaden för att generera mutanterna och köra varje testfall mot vart och ett av dem.
Mutationstestning vs. Code Rapportering
Hög testtäckning bevisar inte starka tester. Linje- och grentäckning registrerar vilka satser som kördes, inte om något verifierades efteråt, så ett test som anropar en metod och inte påstår något räknas fortfarande som täckt. Mutationstestning täcker det gapet, eftersom en mutant bara dör när en påstående faktiskt misslyckas.
| Aspect | Code täckning | Mutationspoäng |
| Vad den mäter | Vilka linjer eller grenar testerna körde | Vilka injicerade fel testerna upptäckte |
| Känslig för påståenden | Nej — ett test med noll påståenden ger fortfarande täckning | Ja — en mutant överlever när inget påstående misslyckas |
| Kostnaden för en körning | En instrumenterad testkörning | En testkörning per överlevande mutant, hittills betydligt långsammare |
| Typisk användning | En snabb grind vid varje commit | En djupare periodisk kontroll av kritiska moduler |
| Feltillstånd | 100 procents täckning utan någon verklig verifiering | Motsvarande mutanter som aldrig kan dödas |
De två mätvärdena kompletterar varandra. Täckningen anger koden som aldrig nåddes; mutationspoängen anger den nådda koden som aldrig kontrollerades. Båda matar samma process för felhantering, vid sidan av åtgärder som defektdensitet.
Fördelar med mutationstestning
Följande är fördelarna med mutationstestning:
- Det är en kraftfull metod för att uppnå hög täckning av källprogrammet.
- Den testar själva testsviten, vilket ingen annan teknik för mjukvarutestning gör direkt.
- Mutationstestning ger en god nivå av feldetektering till mjukvaruutvecklaren.
- Metoden avslöjar oklarheter i källkoden och har kapacitet att exponera fel som vanliga körningar aldrig når.
- Överlevande mutanter är handlingsbara: var och en namnger en specifik linje och en specifik förändring som sviten inte märkte.
- Kunderna drar nytta av denna testning genom att få ett mer tillförlitligt och stabilt system.
Nackdelar med mutationstestning
Å andra sidan finns det följande nackdelar med mutationstestning:
- Mutationstestning är extremt kostsamt och tidskrävande, eftersom ett stort antal mutantprogram måste genereras och kompileras.
- Eftersom det är tidskrävande är det rimligt att säga att denna testning inte kan göras utan ett automatiseringsverktyg.
- Varje mutant utövas av samma antal testfall som det ursprungliga programmet, så en stor mutantpopulation måste köras mot hela testsviten.
- Likvärdiga mutanter kan inte dödas med något test, och att separera dem från äkta överlevande kräver vanligtvis manuell granskning.
- Eftersom metoden ändrar källkoden är den inte tillämplig på Svart Box Testning.
När man ska använda mutationstestning
Kostnadsprofilen ovan innebär att mutationstestning sällan körs över en hel kodbas vid varje commit. Det betalar sig självt där ett oupptäckt fel är dyrt och koden som testas är tillräckligt liten för att mutera snabbt.
- Säkerhetskritisk eller ekonomisk logik — betalningsberäkning, skatteregler och auktoriseringskontroller, där ett tyst felaktigt svar är värre än en krasch.
- Sviter med misstänkt hög täckning — när täckningen är nära 100 procent men defekterna fortfarande finns kvar.
- Äldre kod omstruktureras — mutationsresultaten avslöjar om de befintliga testerna skulle upptäcka en regression.
- Bibliotek och delade komponenter — ett fel i en återanvänd komponent multipliceras över varje uppringare.
- Lag som övar testdriven utveckling — poängen kontrollerar att de prov som skrivs först gör riktigt arbete.
Det är vanligtvis inte värt att köra på engångsprototyper, på tunt lim eller genererad kod utan förgreningslogik, eller på sviter som domineras av långsamma integrationstester som redan tar timmar för ett enda pass.
De flesta team avgränsar därför körningen till ändrade filer, sätter ett tröskelvärde för de moduler som är viktiga och låter det bredare regressionstestning sviten bär resten av livscykel för mjukvarutestning.



