Mutationstest med PITest

1. Oversigt

Softwaretest refererer til de teknikker, der bruges til at vurdere funktionaliteten af ​​en softwareapplikation. I denne artikel skal vi diskutere nogle af de målinger, der bruges i softwaretestindustrien, såsom kode dækning og mutationstest, med en særlig interesse for, hvordan man udfører en mutationstest ved hjælp af PITest bibliotek.

Af hensyn til enkelheden vil vi basere denne demonstration på en grundlæggende palindromfunktion - Bemærk, at en palindrom er en streng, der læser det samme baglæns og fremad.

2. Maven-afhængigheder

Som du kan se i Maven-afhængighedskonfigurationen, bruger vi JUnit til at køre vores tests og PITest bibliotek at introducere mutanter ind i vores kode - rolig, vi vil se om et sekund, hvad en mutant er. Du kan altid slå den nyeste afhængighedsversion op mod maven central repository ved at følge dette link.

 org.pitest pitest-parent 1.1.10 pom 

For at have PITest-biblioteket i gang, skal vi også medtage bedrøveligste-maven plugin i vores pom.xml konfigurationsfil:

 org.pitest pitest-maven 1.1.10 com.baeldung.testing.mutation. * com.baeldung.mutation.test. * 

3. Opsætning af projekt

Nu hvor vi har konfigureret vores Maven-afhængigheder, lad os se på denne selvforklarende palindromfunktion:

public boolean isPalindrome (String inputString) {if (inputString.length () == 0) {return true; } andet {char firstChar = inputString.charAt (0); char lastChar = inputString.charAt (inputString.length () - 1); String mid = inputString.substring (1, inputString.length () - 1); return (firstChar == lastChar) && isPalindrome (mid); }} 

Alt hvad vi har brug for nu er en simpel JUnit-test for at sikre, at vores implementering fungerer på den ønskede måde:

@Test offentligt ugyldigt nårPalindrom_thenAccept () {Palindrome palindromeTester = new Palindrome (); assertTrue (palindromeTester.isPalindrome ("middag")); } 

Indtil videre er vi klar til at køre vores testcase med succes som en JUnit-test.

Dernæst i denne artikel vil vi fokusere på kode og mutationsdækning ved hjælp af PITest-biblioteket.

4. Kodedækning

Kodedækning er blevet udbredt i softwareindustrien til at måle, hvor stor en procentdel af udførelsesstier er blevet udøvet under automatiserede tests.

Vi kan måle den effektive kodedækning baseret på eksekveringsstier ved hjælp af værktøjer som f.eks Eclemma tilgængelig på Eclipse IDE.

Efter løb TestPalindrome med kodedækning kan vi nemt opnå en 100% dæknings score - Bemærk det er Palindrome er rekursivt, så det er ret indlysende, at tom inputlængdekontrol alligevel vil blive dækket.

Desværre kan kode dækningsmålinger undertiden være ret ineffektiv, fordi en 100% kode dækning score kun betyder, at alle linjer blev udøvet mindst en gang, men det siger intet om test nøjagtighed eller fuldstændighed i brugssager, og det er derfor, mutationstest faktisk betyder noget.

5. Mutationsdækning

Mutationstest er en testteknik, der bruges til forbedre tilstrækkeligheden af tests og identificere mangler i kode. Ideen er at ændre produktionskoden dynamisk og få testene til at mislykkes.

Gode ​​tests skal mislykkes

Hver ændring i koden kaldes a mutant, og det resulterer i en ændret version af programmet, kaldet a mutation.

Vi siger, at mutationen er dræbt hvis det kan forårsage en fejl i testene. Vi siger også, at mutationen overlevede hvis mutanten ikke kunne påvirke testens opførsel.

Lad os nu køre testen ved hjælp af Maven med målindstillingen indstillet til: org.pitest: pitest-maven: mutationCoverage.

Vi kan kontrollere rapporterne i HTML-format i mål / pit-test / ÅÅÅÅMDDHHMI vejviser:

  • 100% linjedækning: 7/7
  • 63% mutationsdækning: 5/8

Det er klart, at vores test fejer på tværs af alle udførelsesstier, således at linjedækningsresultatet er 100%. På den anden side introducerede PITest-biblioteket 8 mutanter, 5 af dem blev dræbt - Forårsagede en fiasko - men 3 overlevede.

Vi kan kontrollere com.baeldung.testing.mutation / Palindrome.java.html rapport for flere detaljer om de oprettede mutanter:



Disse er de mutatorer aktive som standard når du kører en mutationsdækningstest:

  • INCREMENTS_MUTATOR
  • VOID_METHOD_CALL_MUTATOR
  • RETURN_VALS_MUTATOR
  • MATH_MUTATOR
  • NEGATE_CONDITIONALS_MUTATOR
  • INVERT_NEGS_MUTATOR
  • CONDITIONALS_BOUNDARY_MUTATOR

For flere detaljer om PITest-mutatorer kan du tjekke embedsmanden dokumentationsside link.

Vores mutationsdækningsscore afspejler manglen på testsager, da vi ikke kan sikre os, at vores palindromfunktion afviser ikke-palindromiske og næsten palindromiske strenginput.

6. Forbedre mutationsscore

Nu hvor vi ved, hvad en mutation er, er vi nødt til at forbedre vores mutationsscore med dræber de overlevende mutanter.

Lad os tage den første mutation - negeret betinget - på linje 6 som et eksempel. Mutanten overlevede, for selvom vi ændrer kodestykket:

hvis (inputString.length () == 0) {returner sand; }

Til:

hvis (inputString.length ()! = 0) {returner sand; }

Testen vil bestå, og det er derfor mutationen overlevede. Ideen er at implementere en ny test, der mislykkes, hvis mutanten introduceres. Det samme kan gøres for de resterende mutanter.

@Test offentlig ugyldig nårNotPalindrom_thanReject () {Palindrome palindromeTester = new Palindrome (); assertFalse (palindromeTester.isPalindrome ("boks")); } @Test offentlig ugyldigt nårNearPalindrom_thanReject () {Palindrome palindromeTester = new Palindrome (); assertFalse (palindromeTester.isPalindrome ("neon")); }

Nu kan vi køre vores tests ved hjælp af mutationsdækningspluginet for at sikre, at det alle mutationer blev dræbt, som vi kan se i PITest-rapporten genereret i målmappen.

  • 100% linjedækning: 7/7
  • 100% mutationsdækning: 8/8

7. PITest-testkonfiguration

Mutationstest kan undertiden være omfattende ressourcer, så vi er nødt til at indføre korrekt konfiguration for at forbedre testens effektivitet. Vi kan gøre brug af targetClasses tag, for at definere listen over klasser, der skal muteres. Mutationstest kan ikke anvendes på alle klasser i et virkeligt verdensprojekt, da det vil være tidskrævende og ressourcekritisk.

Det er også vigtigt at definere de mutatorer, du planlægger at bruge under mutationstest, for at minimere de nødvendige computerressourcer til at udføre testene:

  com.baeldung.testing.mutation. * com.baeldung.mutation.test. * CONSTRUCTOR_CALLS VOID_METHOD_CALLS RETURN_VALS NON_VOID_METHOD_CALLS 

Desuden tilbyder PITest-biblioteket en række muligheder, der er tilgængelige for tilpas dine teststrategier, kan du angive det maksimale antal mutanter, der introduceres efter klasse ved hjælp af maxMutationsPerClass mulighed for eksempel. Flere detaljer om PITest-muligheder i den officielle Maven hurtigstartguide.

8. Konklusion

Bemærk, at kodedækning stadig er en vigtig metrik, men nogle gange er det ikke tilstrækkeligt til at garantere en velafprøvet kode. Så i denne artikel har vi gået igennem mutationstest som en mere sofistikeret måde at sikre testkvalitet og godkende testsager ved hjælp af PITest bibliotek.

Vi har også set, hvordan man analyserer en grundlæggende PITest-rapporter, mens vi forbedrer mutationsdækning score.

Selvom mutationstest afslører mangler i kode, skal den bruges klogt, fordi det er ekstremt kostbar og tidskrævende proces.

Du kan tjekke eksemplerne i denne artikel i det linkede GitHub-projekt.