Lügen Ihre Tests? Zeit für Mutation Testing
\n\nSie schreiben Unit-Tests, die Code Coverage sieht gut aus – 85%, 90% – und trotzdem tauchen Bugs in der Produktion auf? Ein bekanntes Problem. Die klassische Metrik (Line/Branch Coverage) misst im Grunde nur, welche Codezeilen ausgeführt wurden. Sie sagt aber nichts darüber aus, wie gründlich die Tests die Logik prüfen. Sie können einfach die Zeilen "durchlaufen", ohne die Korrektheit des Ergebnisses zu verifizieren.
\n\nHier kommt Mutation Testing ins Spiel. Stellen Sie es sich wie eine Impfung für Ihren Code vor: Wir schleusen absichtlich kleine "Viren" (Mutationen) in das Programm ein und beobachten, ob unsere Tests sie erkennen. Fallen die Tests nicht, nachdem wir === in !== geändert haben – sind Ihre Tests schwach. In diesem Artikel auf pythonlib.ru zeigen wir Ihnen, wie Sie Mutation Testing in Ihr JavaScript-Projekt integrieren, um Ihren Code wirklich robust zu machen.
Was ist Mutation Testing? Einfach erklärt
\n\nStellen Sie sich vor, Sie sind ein QA-Ingenieur, der heimlich eine Zeile im Code des Entwicklers ändert:
\n\n// Vorher\nif (user.age >= 18) { allowAccess(); }\n\n// Nachher (Mutation)\nif (user.age > 18) { allowAccess(); }\n\nWenn die Unit-Tests nach dieser Änderung immer noch grün sind – dann prüfen die Tests den Grenzfall 18 nicht. Ein Mutations-Framework führt tausende solcher Änderungen automatisch durch und erstellt einen Bericht: Wie viele Mutationen haben überlebt (survived). Je weniger überlebende Mutanten, desto zuverlässiger Ihre Tests.
Die wichtigsten Mutationsarten in JavaScript:
\n- \n
- Änderung von Vergleichsoperatoren:
>→>=,===→!==\n - Entfernen von Funktionsaufrufen:
validate()→ leere Zeile \n - Änderung numerischer Literale:
0→1,100→0\n - Invertieren von logischen Bedingungen:
if (isActive)→if (!isActive)\n - Ersetzen von Zeichenketten (seltener, aber möglich) \n
Warum Code Coverage eine trügerische Sicherheit ist
\n\nViele JavaScript-Entwickler wiegen sich in falscher Sicherheit bei 100% Code Coverage. Sehen wir uns ein reales Beispiel an:
\n\n// Funktion zur Rabattberechnung\nfunction getDiscount(price, isPremium) {\n if (price > 100 && isPremium) {\n return price * 0.8;\n }\n return price;\n}\n\nUnd der "perfekte" Test:
\n\ntest('should return price for non-premium', () => {\n expect(getDiscount(50, false)).toBe(50);\n});\n\ntest('should return price for premium with low price', () => {\n expect(getDiscount(50, true)).toBe(50);\n});\n\nCoverage: 100% Zeilen, 100% Zweige. Aber die Mutation price > 100 → price >= 100 wird überleben! Die Tests prüfen den Grenzwert 100.01 oder exakt 100 nicht. Mutation Testing deckt diese "blinden Flecken" sofort auf.
Tool Nr. 1: Stryker Mutator für JavaScript
\n\nDas bekannteste und ausgereifteste Tool für Mutation Testing in der JS-Welt ist Stryker Mutator (oder einfach Stryker). Es unterstützt Jest, Mocha, Jasmine, Karma und funktioniert mit TypeScript.
\n\nInstallation und Schnellstart
\n\nInstallieren Sie Stryker global oder lokal in Ihrem Projekt:
\n\nnpm install --save-dev @stryker-mutator/core @stryker-mutator/jest-runner\n\nErstellen Sie eine Konfigurationsdatei stryker.conf.json:
{\n "$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",\n "packageManager": "npm",\n "reporters": ["html", "clear-text", "progress"],\n "testRunner": "jest",\n "coverageAnalysis": "perTest",\n "thresholds": {\n "high": 80,\n "low": 60,\n "break": 50\n }\n}\n\nStarten Sie den Mutations