Mutationstests in Go: Wie man das findet, was Unit-Tests übersehen

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

Einleitung: Tests bestehen, aber Bugs bleiben?

\n\n

Stellen Sie sich folgende Situation vor: Sie haben ein Modul in Go geschrieben, es mit Unit-Tests zu 95% abgedeckt (laut go test -cover), alle Tests sind grün. Sie sind überzeugt, dass der Code einwandfrei funktioniert. Aber in der Produktion taucht ein Bug auf. Kommt Ihnen das bekannt vor?

\n\n

Das Problem ist, dass die Metrik der Codeabdeckung (Code Coverage) lediglich die Anzahl der ausgeführten Zeilen misst. Sie sagt nichts darüber aus, was die Tests tatsächlich prüfen. Man kann 100% Abdeckung haben, aber nie überprüfen, ob eine Funktion den richtigen Wert zurückgibt, sondern nur, dass sie nicht mit einer Panic abstürzt.

\n\n

Hier kommt Mutationstests (Mutation Testing) in Go ins Spiel – eine Methode, die künstlich Fehler in Ihren Code einbaut und prüft, ob die Tests diese erkennen können. Wenn die Tests beim mutierten Code nicht fehlschlagen, sind Ihre Tests nutzlos.

\n\n

In diesem Artikel erklären wir, was Mutationstests in Go sind, wie man sie einrichtet und warum sie ein Must-have für Projekte sind, bei denen Zuverlässigkeit kritisch ist. Los geht's!

\n\n

Was sind Mutationstests?

\n\n

Mutationstests sind eine Methode zur Bewertung der Testqualität, bei der kleine Änderungen (Mutationen) in das Programm eingefügt werden, die typische Entwicklerfehler simulieren. Anschließend werden die Tests ausgeführt. Wenn mindestens ein Test fehlschlägt, ist die Mutation "getötet" – der Test funktioniert. Wenn alle Tests bestehen, hat die Mutation "überlebt", und das ist eine rote Flagge: Sie haben ein Loch in Ihren Tests.

\n\n

In Go werden Mutationstests oft als Mutation Testing Golang bezeichnet, und sie sind besonders nützlich für die Überprüfung von Geschäftslogik, Validatoren, Parsern und anderen kritischen Komponenten.

\n\n

Beispiel einer Mutation

\n\n

Angenommen, Sie haben eine einfache Funktion:

\n\n
func IsEven(n int) bool {\n    return n%2 == 0\n}
\n\n

Eine typische Mutation ist das Ersetzen von == durch !=:

\n\n
func IsEven(n int) bool {\n    return n%2 != 0\n}
\n\n

Wenn Ihr Test nur prüft, dass IsEven(2) nicht abstürzt, aber nicht, dass das Ergebnis true ist, wird die Mutation überleben. Der Test besteht, obwohl die Funktion jetzt genau das Gegenteil tut.

\n\n

Warum reicht einfache Codeabdeckung nicht aus?

\n\n

Viele Go-Entwickler verlassen sich auf go test -cover. Das ist eine nützliche Metrik, aber sie kann täuschen. Betrachten wir ein Beispiel:

\n\n
func Divide(a, b int) (int, error) {\n    if b == 0 {\n        return 0, errors.New("division by zero")\n    }\n    return a / b, nil\n}
\n\n

Test:

\n\n
func TestDivide(t *testing.T) {\n    result, err := Divide(10, 2)\n    if err != nil {\n        t.Fatal(err)\n    }\n    if result != 5 {\n        t.Errorf("expected 5, got %d", result)\n    }\n}
\n\n

Abdeckung? 100% – alle Zeilen wurden ausgeführt. Aber was, wenn jemand versehentlich a / b durch a * b ersetzt? Der Test bemerkt es nicht, weil 10 * 2 = 20 ist, wir aber 5 erwartet haben. Schlägt der Test fehl? Nein, wenn wir nicht den genauen Wert prüfen.

\n\n

Mutationstests in Go würden dieses Problem sofort aufdecken, indem sie einen Mutanten mit Multiplikation erstellen und prüfen, ob der Test fehlschlägt.

\n\n

Werkzeuge für Mutationstests in Go

\n\n

Das wichtigste Werkzeug ist go-mutesting. Es ist eine beliebte Bibliothek, die automatisch Mutationen erstellt und Tests ausführt. Installation:

\n\n
go get github.com/zimmski/go-mutesting/cmd/go-mutesting
\n\n

Es gibt auch Alternativen wie mutesting von der Firma "Go Test It", aber go-mutesting ist der De-facto-Standard.

\n\n

Wie man es ausführt

\n\n

Basierender Aufruf:

\n\n
go-mutesting ./...
\n\n

Dieser Befehl findet alle _test.go-Dateien im Projekt und prüft, welche Mutationen überleben. Das Ergebnis wird mit einem detaillierten Bericht in der Konsole ausgegeben.

\n\n

Praktisches Beispiel: Mutationstests in Go

\n\n

Erstellen wir ein einfaches Paket calculator mit Additions- und

Blogs

Buchempfehlungen