Mutation Testing
Code Coverage sagt, welcher Anteil des Codes durch Tests ausgeführt wird. Sie sagt aber nicht, ob die Tests den Code auch wirklich auf Fehler überprüfen. Mutation Testing schließt diese Lücke, indem es kleine Veränderungen am Quellcode einfügt und prüft, ob die Tests diese erkennen. Überlebt eine Mutation, also eine künstliche Fehlerquelle, den Testlauf, ist der Test offensichtlich nicht aussagekräftig genug.
In a Nutshell
- Mutation Testing fügt künstliche Fehler in den Quellcode ein.
- Ziel ist es, die Güte der Tests zu messen, nicht nur deren Abdeckung.
- Mutation Score = getötete Mutanten / alle Mutanten.
- Hoher Rechenaufwand ist die größte Herausforderung.
Kompakte Fachbeschreibung
Beim Mutation Testing werden Mutanten erzeugt, das sind leicht veränderte Versionen des Produktivcodes. Eine Mutante kann beispielsweise einen arithmetischen Operator ändern, eine Bedingung umkehren oder einen Rückgabewert manipulieren. Die Test suite wird gegen jede Mutante ausgeführt. Schlägt mindestens ein Test fehl, wurde die Mutante getötet. Bestehen alle Tests, hat die Mutante überlebt, was auf schwache Tests hinweist.
Mutation Score
Der Mutation Score berechnet sich wie folgt:
Mutation Score = (Getötete Mutanten / Anzahl aller Mutanten) * 100
Ein hoher Score bedeutet, dass die Tests Fehler im Code zuverlässig erkennen. Ein niedriger Score zeigt, dass Tests zwar ausgeführt werden, aber keine wirksame Prüfung durchführen.
Arten von Mutationen
Arithmetische Operatoren
int result = a + b; // Mutante: a - b
Relationale Operatoren
if (x > 10) { ... } // Mutante: x < 10
Rückgabewerte
return true; // Mutante: return false;
Statement Deletion
System.out.println("log"); // Mutante: Zeile entfernt
Bedingungen
if (a && b) // Mutante: if (a || b)
Wichtige Werkzeuge
- PIT: Mutation Testing für Java und JVM-Sprachen.
- Stryker: Mutation Testing für JavaScript, TypeScript und C#.
- MutPy: Mutation Testing für Python.
- Cosmic Ray: Mutation Testing für Python.
- Infection: Mutation Testing für PHP.
Praxisbeispiel: Einfache Mutation in Java
public class DiscountCalculator {
public int calculate(int amount) {
if (amount > 100) {
return amount - 10;
}
return amount;
}
}
@Test
public void testDiscountForLargeAmount() {
DiscountCalculator calc = new DiscountCalculator();
assertEquals(90, calc.calculate(100)); // Problem: Grenzwert 100 statt 101
}
Eine Mutante ändert amount > 100 zu amount >= 100. Für den Eingabewert 100 ergibt sich in beiden Fällen 90. Der Test überlebt die Mutante, obwohl die Bedingung unterschiedlich ist. Das zeigt, dass der Test den Grenzwert nicht korrekt prüft.
Vorteile und Nachteile
Vorteile
- Bessere Testqualität: Misst, ob Tests wirklich prüfen.
- Weniger falsch positive Coverage: Deckung allein ist kein Qualitätsbeweis.
- Fokus auf kritische Stellen: Schwache Tests werden sichtbar.
- Bessere Testfälle: Entwickler werden gezwungen, aussagekräftigere Assertions zu schreiben.
Nachteile
- Hoher Rechenaufwand: Viele Mutanten erzeugen viele Testläufe.
- Äquivalente Mutanten: Manche Mutationen verändern das Verhalten nicht, können aber nicht automatisch erkannt werden.
- Lange Laufzeit: In großen Projekten kann Mutation Testing Stunden dauern.
- Wartung: Neue Mutanten müssen bei Codeänderungen neu erzeugt werden.
Prüfungsrelevante Stichpunkte
- Definition und Zweck von Mutation Testing.
- Mutation Score und Berechnung.
- Arten von Mutationen.
- Werkzeuge wie PIT, Stryker, MutPy.
- Unterschied zwischen Coverage und Mutation Testing.
- Probleme äquivalenter Mutanten.
Typische Prüfungsfragen (mit Kurzantwort)
-
Was ist Mutation Testing? Eine Methode, die künstliche Fehler in den Code einfügt, um die Güfe der Tests zu messen.
-
Was sagt der Mutation Score aus? Den Anteil der Mutanten, die durch die Tests erkannt und getötet wurden.
-
Was ist eine äquivalente Mutante? Eine Mutation, die das Verhalten des Programms nicht verändert, aber trotzdem nicht getötet wird.
-
Nenne ein Mutation Testing Werkzeug. PIT, Stryker, MutPy oder Infection.
-
Was ist der Unterschied zwischen Coverage und Mutation Testing? Coverage misst, welcher Code ausgeführt wird. Mutation Testing prüft, ob die Tests echte Fehler erkennen.
Wichtigste Quellen
Häufig gestellte Fragen
Was ist eine Mutante?
Eine Mutante ist eine leicht veränderte Version des Quellcodes, die einen künstlichen Fehler repräsentiert.
Was bedeutet getötete Mutante?
Eine Mutante ist getötet, wenn mindestens ein Test nach der Änderung fehlschlägt. Das zeigt, dass der Test den Fehler erkannt hat.
Was ist ein guter Mutation Score?
Ein Score über 80 Prozent gilt oft als gut. Wichtiger als die Zahl ist jedoch, dass die Tests die wirklich kritischen Fehler erkennen.
Warum ist Mutation Testing rechenintensiv?
Für jede Mutante muss die gesamte Test suite erneut ausgeführt werden. Bei vielen Mutanten und großen Codebasen entsteht ein enormer Zeitaufwand.
Was sind äquivalente Mutanten?
Äquivalente Mutanten ändern den Code, ohne das Verhalten zu verändern. Sie können nicht automatisch erkannt werden und verfälschen den Mutation Score.
Ist Mutation Testing nur für Unit Tests geeignet?
Primär ja, da Unit Tests isoliert und schnell sind. Für Integrationstests ist Mutation Testing wegen der Laufzeit und Komplexität weniger verbreitet.
Wann sollte Mutation Testing eingesetzt werden?
Sinnvoll ist es in stabilen, kritischen Codebereichen, wenn die Coverage bereits hoch ist, aber die Qualität der Tests unklar bleibt.
Was ist ein typisches Mutation Testing Anti-Pattern?
Tests mit Assertions zu schreiben, die fast jede Änderung tolerieren, weil sie zu generisch oder zu schwach sind.
Kann Mutation Testing in der CI/CD-Pipeline laufen?
Ja, aufgrund der Laufzeit wird es oft als separater Nightly- oder Weekly-Job ausgeführt, nicht bei jedem Commit.
Was ist Stryker?
Stryker ist ein populäres Mutation Testing Framework für JavaScript, TypeScript und C#.
Was ist PIT?
PIT ist ein weit verbreitetes Mutation Testing Tool für Java und andere JVM-basierte Sprachen.
Ersetzt Mutation Testing Code-Coverage?
Nein. Es ergänzt Coverage, indem es die Aussagekraft der Tests misst. Beide Metriken zusammen geben ein besseres Bild.
Was bedeutet Mutation Operator?
Ein Mutation Operator ist eine Regel, die beschreibt, wie der Code verändert wird, um eine Mutante zu erzeugen, beispielsweise das Umkehren eines Vergleichsoperators.
Wie kann man die Laufzeit von Mutation Testing reduzieren?
Durch parallele Ausführung, inkrementelles Mutation Testing, Fokus auf kritische Codebereiche und das Ausschließen trivialer oder generierter Dateien.
Was zeigt eine überlebende Mutante?
Eine überlebende Mutante zeigt, dass die Test suite den eingefügten Fehler nicht erkannt hat. Der Test ist dann zu schwach oder prüft das falsche Verhalten.
Weiter im Software Testing Lernpfad
Der nächste Artikel im Software Testing Lernpfad behandelt Shift-Left Testing — wie Tests früher im Entwicklungsprozess platziert werden.



