Warum Testing in Go nicht beängstigend, sondern notwendig ist
\n\nWenn Sie zum ersten Mal in Go schreiben, scheint es, als würde der Compiler fast alles abfangen: strenge Typen, explizite Fehler, nil unter Kontrolle. Aber sobald Ihr Projekt auf über 500 Zeilen anwächst, wird Ihnen klar: Ohne Tests kann man nicht leben. Eine Änderung an einer Stelle zerstört eine andere, und Sie erfahren davon erst in der Produktion.
In diesem Artikel zeige ich Ihnen, wo ein Anfänger mit dem Testen in Go beginnen kann. Wir befassen uns mit Unit-Tests, tabellarischen Tests, Mocks und Code Coverage. Kein unnötiges Gerede – nur funktionierende Beispiele und praktische Ratschläge.
\n\nWenn Sie seit mindestens ein paar Monaten in Go schreiben, spart Ihnen dieses Material Stunden beim Debuggen.
\n\nWas Sie vor dem Start wissen müssen
\n\nGo bietet ein integriertes Paket testing – es muss nichts zusätzlich installiert werden. Tests werden in Dateien mit dem Suffix _test.go geschrieben und mit dem Befehl ausgeführt:
go test ./...\n\nDie grundlegenden Regeln:
\n- \n
- Die Testfunktion muss
TestXxx(t *testing.T)heißen, wobei Xxx der Name der zu testenden Funktion in Großschreibung ist. \n - Testdateien liegen neben dem zu testenden Code. \n
- Fehler werden über
t.Error()odert.Fatal()gemeldet. \n
Kommen wir direkt zur Praxis.
\n\nUnseren ersten Unit-Test in Go schreiben
\n\nAngenommen, wir haben eine einfache Funktion, die zwei Zahlen summiert:
\n\n// calc.go\npackage calc\n\nfunc Sum(a, b int) int {\n return a + b\n}\n\nErstellen Sie die Datei calc_test.go im selben Ordner:
// calc_test.go\npackage calc\n\nimport "testing"\n\nfunc TestSum(t *testing.T) {\n result := Sum(2, 3)\n expected := 5\n\n if result != expected {\n t.Errorf("Sum(2,3) = %d; want %d", result, expected)\n }\n}\n\nFühren Sie den Test aus:
\n\ngo test -v\n\nSie sehen ein grünes PASS – Ihr erster Testsieg. Beachten Sie: Wir verwenden t.Errorf (nicht fatal) – der Test wird fortgesetzt, auch wenn ein Fall fehlschlägt.
Tabellarische Tests: Wenn ein einzelner Test unpraktisch ist
\n\nStellen Sie sich vor, Sie müssen eine Funktion mit einem Dutzend verschiedener Eingabedaten testen. 10 separate Tests schreiben? Nein, in Go gibt es dafür tabellarische Tests (table-driven tests). Dies ist der Standard in der Go-Community.
\n\nFügen wir in unserer calc.go eine Divisionsfunktion hinzu:
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\nJetzt schreiben wir einen tabellarischen Test:
\n\nfunc TestDivide(t *testing.T) {\n tests := []struct {\n name string\n a, b int\n expected int\n wantErr bool\n }{\n {"positive", 10, 2, 5, false},\n {"negative", -6, 3, -2, false},\n {"zero division", 5, 0, 0, true},\n {"same numbers", 7, 7, 1, false},\n }\n\n for _, tt := range tests {\n t.Run(tt.name, func(t *testing.T) {\n got, err := Divide(tt.a, tt.b)\n if tt.wantErr {\n if err == nil {\n t.Error("expected error, got nil")\n }\n return\n }\n if err != nil {\n t.Errorf("unexpected error: %v", err)\n }\n if got != tt.expected {\n t.Errorf("Divide(%d,%d) = %d; want %d", tt.a, tt.b, got, tt.expected)\n }\n })\n }\n}\n\nWas hier großartig ist:
\n- \n
- Jeder Fall ist ein separater Untertest mit einem Namen. Wenn einer fehlschlägt, werden die anderen ausgeführt. \n
- Neue Fälle lassen sich leicht hinzufügen – Sie fügen einfach eine Zeile in die Struktur ein. \n
- Der Code liest sich wie eine Spezifikation. \n
Führen Sie es mit dem Flag -v aus – Sie sehen die Namen aller Untertests. Das ist beim Debuggen sehr praktisch.