Mutationstests mit Stryker: Warum Zeilen-Codeabdeckung beim Testen von Software allein nicht ausreicht
In der Softwareentwicklung ist die Messung der Zeilenabdeckung (auch Code Coverage genannt) ein wichtiger Faktor, um sicherzustellen, dass der Code ordnungsgemäß funktioniert und potenzielle Fehler vermieden werden. Zeilenabdeckung ist ein Maß dafür, wie viele Codezeilen in einem Programm von Tests abgedeckt werden. Es gibt jedoch Situationen, in denen die alleinige Verwendung dieses Messwertes nicht ausreicht, um sicherzustellen, dass der Code fehlerfrei ist. Mutationstests können dabei helfen, dieses Problem zu lösen.
In diesem Artikel werden wir uns anschauen, wie Mutationstests funktionieren und welche Vorteile sie bei der Code-Analyse erbringen können. Anschließend werden wir uns die Technologie Stryker als eine Möglichkeit für die Implementierung von Mutationstest in TypeScript Projekten anschauen.
Betrachten wir zuerst das Problem herkömmlicher Zeilenabdeckung: Nehmen wir an, wir möchten die folgende Funktion addXAndY()
testen. Obwohl die Zeilenabdeckung 100 % beträgt, gibt es einen Fehler in der Funktion, den wir durch unseren regulären Test nicht aufdecken:
function addXAndY(x, y) { return x + x; } // Dieser Test hat eine Code-Zeilenabdeckung von 100 %, deckt den Fehler aber trotzdem nicht auf. it('Test mit unzureichender Ergebnisüberprüfung', () => { let result = addXAndY(1, 1); expect(result).toEqual(2); });
Zeilenabdeckung als Messwert berücksichtigt nicht, ob die Tests tatsächlich effektiv sind oder nicht. Es kann sein, dass ein Test zwar eine Codezeile vollständig abdeckt, aber dennoch nicht alle möglichen Szenarien oder Bedingungen berücksichtigt, welche im Code auftreten können. Dies kann dazu führen, dass ein Test zwar eine hohe Zeilenabdeckung besitzt, aber dennoch viele Szenarien ungetestet blieben.
Hier kommen Mutationstests ins Spiel. Mutationstests sind eine Methode, um die Qualität von Tests zu überprüfen. Hierbei wird der Code absichtlich durch sogenannte Mutationen verändert, um zu prüfen, ob die vorhandenen Tests Fehler erkennen können. Dieses Prinzip ist in der linken Grafik dargestellt.
Wenn ein Test fehlschlägt, nachdem eine Veränderung am Code vorgenommen wurde, gilt er als erfolgreich, da er die Veränderung erkannt hat. Wenn jedoch ein Test bestanden wird, obwohl eine Veränderung am Code vorgenommen wurde, deutet dies darauf hin, dass der Test unzureichend ist und nicht alle möglichen Szenarien abdeckt.
Im Folgenden wenden wir dieses Prinzip an der addXAndY()
Funktion von eben an:
function addXAndY(x, y) { y = null; // eingefügte Mutation return x + x; } it('Test mit unzureichender Ergebnis-Überprüfung', () => { let result = addXAndY(1, 1); expect(result).toEqual(2); // Der Test schlägt trotz Mutation nicht fehl. Dies zeigt, dass der Test unzureichend geschrieben ist. });
An diesem Beispiel können wir den Vorteil von Mutationstests sehen: Der Rechenfehler in der Funktion addXAndY()
wird aufgedeckt.
Zur Implementierung von Mutationstests existieren eine Reihe von Frameworks. Diese Frameworks sind spezielle Tools oder Bibliotheken, die der Automatisierung des Mutationsprozesses von Code dienen.
Für JavaScript/TypeScript Projekte kann Stryker Mutator verwendet werden. Stryker spielt automatisch Mutationen in den Code ein und überprüft diese anhand der bestehenden Unit-Tests. Stryker kann in wenigen Schritten in jedes NPM-Projekt eingebunden werden:
- Installieren Sie Stryker als Abhängigkeit in deinem Projekt:
npm install --save-dev @stryker-mutator/core
- Erstellen Sie eine Konfigurationsdatei für Stryker. Sie können dies manuell tun oder Stryker verwenden, um eine Vorlage zu erstellen:
npm exec stryker init
Dieser Befehl erstellt eine stryker.conf.js-Datei im Hauptverzeichnis Ihres Projekts. In dieser Datei können Sie Stryker an Ihre Bedürfnisse anpassen, indem Sie beispielsweise Testrahmenwerke, Mutationsoperationen, Testausführungsoptionen und vieles mehr konfigurieren. - Führen Sie Stryker aus, um Mutationstests durchzuführen:
npm exec stryker run
Dieser Befehl führt Stryker mit der Konfiguration aus, die in der stryker.conf.js-Datei definiert ist.
Fazit
Zusammenfassend lässt sich festhalten, dass Zeilenabdeckung allein nicht ausreicht, um sicherzustellen, dass ein Programmcode fehlerfrei ist. Mutationstests können dabei helfen, Lücken in der Testabdeckung aufzudecken und Entwicklern unterstützen, sich auf die Schreibweise von sauberem und robustem Code zu konzentrieren. Indem sie Mutationstests verwenden, können Entwickler sicherstellen, dass der Code nicht nur effektiv getestet wird, sondern auch widerstandsfähig gegen unvorhergesehene Ereignisse ist.
Seminarempfehlung
TYPESCRIPT GRUNDLAGEN E-TYPSC-01
Mehr erfahrenJunior Consultant bei ORDIX
Bei Updates im Blog, informieren wir per E-Mail.
Kommentare