Mutationstests für C++: Wie man Code unzerstörbar macht

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Mutationstests in C++: Wenn 100% Codeabdeckung eine Lüge ist

\n\n

Sie sind stolz auf Ihre 95% Code Coverage? Vielleicht zu Unrecht. Codeabdeckung ist wie ein Schloss, das man nur durch Rütteln am Griff prüft. Mutationstests hingegen sind der Einbrecher, der wirklich versucht, den Safe zu knacken. In diesem Artikel schauen wir uns an, wie Mutationstests für C++ Ihre Tests von einer Formalität in eine echte Waffe verwandeln. Und ja, wir werden keine langweiligen Definitionen aus Wikipedia kopieren.

\n\n

Stellen Sie sich vor: Sie haben eine Funktion geschrieben, mit Tests abgedeckt, CI ist grün. Und dann geht ein Bug in Produktion. Ein bekanntes Problem? Meistens liegt das Problem nicht im Code, sondern in den Tests, die nicht das Richtige prüfen. Mutation testing für C++ löst genau dieses Problem – es zeigt, wie gefährlich Ihre Tests wirklich für Bugs sind.

\n\n

Was sind Mutationstests und warum braucht ein C++-Entwickler sie?

\n\n

Mutationstests (oder Mutation Testing) sind eine Methode zur Bewertung der Testqualität, bei der kleine Änderungen (Mutationen) in den Quellcode eingefügt werden. Wenn die Tests nach der Mutation immer noch bestanden werden, sind sie schwach. Der Mutant hat überlebt. Wenn die Tests fehlschlagen, wurde der Mutant getötet – der Test funktioniert.

\n\n

Für C++ ist das besonders relevant, weil:

\n
    \n
  • C++-Code oft komplexe Logik mit Zeigern, Templates und RAII enthält.
  • \n
  • Standardmetriken wie Line Coverage und Branch Coverage leicht zu täuschen sind.
  • \n
  • Die Kosten eines Bugs in C++ (insbesondere in Embedded, Finance, Game Engines) katastrophal sein können.
  • \n
\n\n

Ein einfaches Beispiel: Sie haben eine Funktion zur Altersprüfung:

\n\n
bool is_adult(int age) {\n    return age >= 18;\n}
\n\n

Test:

\n\n
TEST(AdultTest, Positive) {\n    EXPECT_TRUE(is_adult(20));\n}
\n\n

Abdeckung: 100% der Zeilen. Aber die Mutation age >= 18 → age > 18 wird nicht erkannt, weil 20 immer noch durchkommt. Der Mutant hat überlebt. Hätten Sie einen Test für is_adult(18) und is_adult(17) gehabt, hätten Sie den Bug gefunden. Mutationstests für C++ zwingen Sie dazu, solche Grenzfälle zu testen.

\n\n

Wichtige Mutationsoperatoren in C++

\n\n

Tools für Mutationstests verwenden eine Reihe von Operatoren (Mutators). Hier sind die häufigsten für C++:

\n\n
    \n
  • Ersetzen arithmetischer Operatoren: + → -, * → /
  • \n
  • Ersetzen logischer Operatoren: && → ||, == → !=
  • \n
  • Ändern von Grenzbedingungen: >= → >, < → <=
  • \n
  • Entfernen von Ausdrücken: Funktionsaufruf wird durch eine Konstante ersetzt
  • \n
  • Ändern des Rückgabewerts: return true → return false
  • \n
  • Mutation von Schleifen: Änderung der Abbruchbedingung oder Schrittweite
  • \n
\n\n

Beispiel mit echtem Code:

\n\n
int factorial(int n) {\n    if (n <= 1) return 1;\n    return n * factorial(n - 1);\n}
\n\n

Mutation #1: n <= 1 → n < 1. Wenn der Test factorial(1) aufruft und 1 erwartet, die Mutation aber den Basisfall entfernt – fällt der Test nur, wenn Sie factorial(0) oder factorial(1) separat prüfen.

\n\n

Mutation #2: return n * factorial(n - 1) → return n + factorial(n - 1). Wenn die Tests nur positive Zahlen abdecken, bemerken sie möglicherweise nicht die Ersetzung von Multiplikation durch Addition.

\n\n

Genau solche Fälle deckt mutation testing in C++ auf.

\n\n

Tools für Mutationstests in C++

\n\n

Nicht alle Tools sind gleich nützlich. Hier sind die drei wichtigsten Akteure auf dem C++-Markt:

\n\n

1. Mull

\n

Ein modernes, aktives Projekt. Arbeitet auf Basis eines LLVM-Passes. Unterstützt C++17/20, integriert sich mit Google Test, Catch2, doctest

Blogs

Buchempfehlungen