Das Paradoxon von 100% Coverage – oder warum Ihre Tests lügen können
\n\nSie haben perfekten Rust-Code geschrieben. Der Compiler ist zufrieden, Clippy schweigt, die Tests laufen grün durch, und der Coverage-Report verkündet stolz: 100% der Zeilen sind abgedeckt. Können Sie aufatmen? Nein. Mutation Testing in Rust ist genau die Technik, die Ihnen zeigt, dass Ihre Tests vielleicht nur die Luft schützen, nicht die Logik.
\n\nStellen Sie sich vor: Sie ändern versehentlich ein + in ein - in einer kritischen Funktion. Die Tests sind immer noch grün? Herzlichen Glückwunsch, Sie haben einen Mutanten gefunden – einen Fehler, der in Ihrem Code lebt, aber von den Tests nicht bemerkt wird. In diesem Artikel zeigen wir, wie Mutation Testing in Rust hilft, solche blinden Flecken zu finden, und integrieren es in ein reales Projekt.
Was ist Mutation Testing (und warum Rust dafür ideal ist)
\n\nMutation Testing (oder Mutations-Testing) ist eine Methode zur Bewertung der Testqualität, bei der kleine künstliche Änderungen (Mutationen) in den Quellcode eingefügt werden. Anschließend werden die Tests ausgeführt. Fallen die Tests beim mutierten Code nicht durch – hat der Mutant überlebt. Je mehr überlebende Mutanten, desto schlechter Ihre Tests.
\n\nRust ist eine Sprache, die bereits ganze Fehlerklassen zur Compile-Zeit abfängt: Race Conditions, Null Pointer, Use-after-Free. Aber Mutation Testing in Rust zielt genau auf die logischen Fehler ab – das, was der Compiler nicht sieht. Das macht Rust zur idealen Plattform: Sie sind bereits gegen 70% der typischen Bugs geschützt, und Mutation Testing erledigt die restlichen 30%.
\n\nWie eine Mutation im Rust-Code aussieht
\n\nTypische Mutationen umfassen:
\n- \n
- Änderung von Operatoren:
a + b→a - b\n - Umkehrung von Bedingungen:
if x > 5→if x < 5\n - Ersetzen von Konstanten:
let n = 42→let n = 0\n - Entfernen von Funktionsaufrufen:
process(data)→ leerer Aufruf \n - Ändern von Iterationsgrenzen:
0..n→0..=n\n
Wenn nach einer solchen Mutation auch nur ein Test nicht durchfällt – haben Sie Ihre Zeit mit dem Schreiben dieses Tests verschwendet. Er prüft nicht das, was er soll.
\n\nWerkzeuge für Mutation Testing in Rust: cargo-mutants
\n\nDas wichtigste Werkzeug für Mutation Testing in Rust ist derzeit cargo-mutants. Es ist ausgereift, wird aktiv gepflegt und liefert lesbare Berichte. Die Installation ist einfach:
cargo install cargo-mutants\n\n\nDanach genügt ein Aufruf:
\n\ncargo mutants\n\n\nDas Tool findet automatisch alle Mutationen in Ihrem Code, führt die Tests aus und erstellt einen HTML-Bericht mit Farbcodierung: Grün – getötete Mutanten (Tests haben angeschlagen), Rot – überlebende Mutanten (Problem erkannt).
\n\nAlternativen auf dem Radar
\n- \n
- mutagen – ein älteres, aber immer noch funktionierendes Tool. Weniger Mutationen, dafür schneller bei kleinen Projekten. \n
- cargo-insta – nicht direkt Mutation Testing, aber ähnlich: Es hilft, Änderungen in Test-Snapshots visuell zu prüfen. \n
Für ernsthafte Arbeit empfehle ich cargo-mutants – es bietet die beste Balance zwischen Geschwindigkeit und Analysetiefe.
Praktisches Beispiel: Einen Rabattrechner testen
\n\nAngenommen, wir haben ein Modul zur Rabattberechnung in einem Online-Shop in Rust:
\n\n// src/discount.rs\n\npub fn calculate_discount(price: f64, customer_age: u32) -> f64 {\n let mut discount = 0.0;\n\n // 10% Rabatt für Rentner (älter als 65)\n if customer_age > 65 {\n discount += 0.1;\n }\n\n // Zusätzlich 5% Rabatt bei Einkäufen ab 1000 Euro\n if price > 1000.0 {\n discount += 0.05;\n }\n\n // Maximaler Rabatt beträgt 30%\n if discount > 0.30 {\n discount = 0.30;\n