# Go Testing 2026: Unit-Tests, Mocks und technische Interviewfragen > Ein umfassender Leitfaden zu Go-Testing-Praktiken im Jahr 2026, einschließlich Unit-Tests mit dem testing-Paket, Mocking mit gomock und häufigen Interviewfragen zu Go-Tests. - Published: 2026-08-24 - Updated: 2026-08-24 - Author: Anthony Fillion-Maillet - Tags: go, testing, unit-tests, mocking, interview - Reading time: 9 min --- Go-Testing basiert auf dem Standard-`testing`-Paket, das mit jeder Go-Installation ausgeliefert wird und keine externen Abhängigkeiten erfordert. Im Gegensatz zu Frameworks in anderen Sprachen setzt Go auf Einfachheit: Testdateien befinden sich neben dem Produktionscode, Testfunktionen folgen einer Namenskonvention, und der `go test`-Befehl übernimmt die Erkennung und Ausführung. > **Worauf Interviewer achten** > > Kandidaten, die tabellengesteuerte Tests schreiben, Interfaces für Dependency Injection verwenden und verstehen, wann Mocking einen Mehrwert bietet und wann es nur Rauschen erzeugt. ## Unit-Tests mit dem testing-Paket schreiben Jede Testdatei endet mit `_test.go` und befindet sich im selben Paket wie der zu testende Code. Testfunktionen beginnen mit `Test` gefolgt von einem großgeschriebenen Namen. Der `*testing.T`-Parameter stellt Methoden zur Meldung von Fehlern bereit. ```go // calculator_test.go package calculator import "testing" func TestAdd(t *testing.T) { result := Add(2, 3) if result != 5 { t.Errorf("Add(2, 3) = %d; want 5", result) } } ``` Die Ausführung von `go test ./...` führt alle Tests im aktuellen Modul aus. Das `-v`-Flag zeigt einzelne Testnamen und deren Bestanden/Nicht-Bestanden-Status an. ## Tabellengesteuerte Tests: Der Go-Standard Tabellengesteuerte Tests reduzieren Duplikation und machen das Hinzufügen neuer Testfälle trivial. Jede Zeile in der Tabelle repräsentiert ein Szenario mit Eingaben und erwarteten Ausgaben. Dieses Muster findet sich in der [Go-Standardbibliothek](https://cs.opensource.google/go/go/+/refs/tags/go1.23.1:src/strings/strings_test.go) und in Produktionscodebases der gesamten Branche. ```go // validator_test.go package validator import "testing" func TestValidateEmail(t *testing.T) { tests := []struct { name string email string wantErr bool }{ {"valid email", "user@example.com", false}, {"missing at sign", "userexample.com", true}, {"empty string", "", true}, {"unicode local part", "user@例え.jp", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidateEmail(tt.email) if (err != nil) != tt.wantErr { t.Errorf("ValidateEmail(%q) error = %v, wantErr %v", tt.email, err, tt.wantErr) } }) } } ``` Die `t.Run`-Methode erstellt Subtests, die unabhängig voneinander laufen. Fehlgeschlagene Subtests melden, welcher spezifische Fall fehlgeschlagen ist, ohne andere Fälle zu stoppen. ## Dependency Injection durch Interfaces Go-Interfaces ermöglichen das Testen von Code, der von externen Systemen abhängt: Datenbanken, HTTP-Clients, Dateisysteme. Die Definition eines minimalen Interfaces am Verwendungsort ermöglicht den Austausch realer Implementierungen gegen Test-Doubles. ```go // service.go package order // Repository definiert die Datenzugriffsmethoden, die dieser Service benötigt type Repository interface { FindByID(id string) (*Order, error) Save(order *Order) error } // Service behandelt die Order-Geschäftslogik type Service struct { repo Repository } // NewService erstellt einen Service mit dem gegebenen Repository func NewService(repo Repository) *Service { return &Service{repo: repo} } // Process validiert und speichert eine Bestellung func (s *Service) Process(order *Order) error { if order.Total <= 0 { return ErrInvalidTotal } return s.repo.Save(order) } ``` Der `Service` akzeptiert jeden Typ, der `Repository` erfüllt. Produktionscode übergibt eine datenbankgestützte Implementierung. Tests übergeben ein Mock oder Stub. ## Mocking mit gomock und mockgen Das [gomock](https://github.com/uber-go/mock)-Paket generiert Mock-Implementierungen aus Interface-Definitionen. Der `mockgen`-Befehl liest Quelldateien und erzeugt Mock-Structs, die Aufrufe aufzeichnen und konfigurierte Werte zurückgeben. ```bash # Mocks für das Repository-Interface generieren mockgen -source=service.go -destination=mocks/repository_mock.go -package=mocks ``` Verwendung des generierten Mocks in Tests: ```go // service_test.go package order import ( "testing" "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "yourmodule/order/mocks" ) func TestService_Process_SavesValidOrder(t *testing.T) { ctrl := gomock.NewController(t) mockRepo := mocks.NewMockRepository(ctrl) order := &Order{ID: "123", Total: 99.99} // Erwarte, dass Save einmal mit dieser Bestellung aufgerufen wird, gib nil zurück mockRepo.EXPECT(). Save(order). Return(nil). Times(1) svc := NewService(mockRepo) err := svc.Process(order) assert.NoError(t, err) } func TestService_Process_RejectsZeroTotal(t *testing.T) { ctrl := gomock.NewController(t) mockRepo := mocks.NewMockRepository(ctrl) // Keine Aufrufe an repo erwartet, da die Validierung zuerst fehlschlägt svc := NewService(mockRepo) err := svc.Process(&Order{ID: "456", Total: 0}) assert.ErrorIs(t, err, ErrInvalidTotal) } ``` Das Mock verifiziert, dass `Save` genau einmal mit dem erwarteten Argument aufgerufen wurde. Wenn der zu testende Code `Save` nie aufruft oder mit anderen Argumenten aufruft, schlägt der Test fehl. ## Verwendung von testify für Assertions und Suites [testify](https://github.com/stretchr/testify) bietet Assertion-Funktionen, die klarere Fehlermeldungen erzeugen als manuelle if-Prüfungen. Das `assert`-Paket setzt die Ausführung nach Fehlern fort. Das `require`-Paket stoppt den Test sofort. ```go // user_test.go package user import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestParseUser(t *testing.T) { input := `{"id": "u1", "name": "Alice"}` user, err := ParseUser(input) require.NoError(t, err, "parsing should not fail") assert.Equal(t, "u1", user.ID) assert.Equal(t, "Alice", user.Name) assert.Empty(t, user.Email, "email should default to empty") } ``` Der `require.NoError`-Aufruf stoppt den Test, wenn das Parsen fehlschlägt, und verhindert Nil-Pointer-Panics bei nachfolgenden Assertions. ## HTTP-Handler mit httptest testen Das `net/http/httptest`-Paket stellt `ResponseRecorder` zum Erfassen der Handler-Ausgabe und `Server` zum Ausführen eines lokalen Testservers bereit. Die meisten Unit-Tests verwenden `ResponseRecorder`, um Netzwerk-Overhead zu vermeiden. ```go // handler_test.go package api import ( "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" ) func TestHealthHandler(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/health", nil) rec := httptest.NewRecorder() HealthHandler(rec, req) assert.Equal(t, http.StatusOK, rec.Code) assert.JSONEq(t, `{"status": "ok"}`, rec.Body.String()) } ``` Für Handler, die externe Dienste aufrufen, wird ein Mock-Client injiziert oder `httptest.NewServer` verwendet, um ein Fake-Backend zu erstellen. ## Häufige Interviewfragen zu Go-Testing Interviewer bewerten Testing-Wissen durch Fragen, die Erfahrung mit realen Codebasen offenbaren. Hier sind Muster, die vorbereitete Kandidaten auszeichnen: **"Wie würden Sie eine Funktion testen, die eine externe API aufruft?"** Definieren Sie ein Interface für den HTTP-Client, injizieren Sie es in die Funktion oder das Struct und stellen Sie ein Mock bereit, das vorbestimmte Antworten zurückgibt. Dies vermeidet Netzwerkaufrufe und macht Tests deterministisch. **"Wann würden Sie ein Stub versus ein Mock verwenden?"** Stubs geben vordefinierte Antworten zurück, ohne zu verifizieren, wie sie aufgerufen wurden. Mocks verifizieren, dass bestimmte Methoden mit bestimmten Argumenten aufgerufen wurden. Verwenden Sie Stubs, wenn der Rückgabewert wichtiger ist als die Interaktion. Verwenden Sie Mocks, wenn die Verifizierung der Interaktion der Zweck des Tests ist. **"Was sind tabellengesteuerte Tests und warum bevorzugt Go sie?"** Tabellengesteuerte Tests definieren Testfälle als Daten in einem Slice von Structs. Sie reduzieren Boilerplate, machen das Hinzufügen von Fällen trivial und erzeugen lesbare Testausgaben mit `t.Run`. Das Muster entspricht Gos Präferenz für expliziten, sich wiederholenden Code gegenüber cleveren Abstraktionen. **"Wie handhaben Sie Test-Fixtures oder Setup, das mehrere Tests teilen?"** Verwenden Sie `TestMain` für Setup und Teardown auf Paketebene. Verwenden Sie Hilfsfunktionen oder Test-Suites (von testify) für gemeinsames Setup über verwandte Tests hinweg. Vermeiden Sie globalen Zustand, der Kopplung zwischen Tests erzeugt. Bereiten Sie sich auf ein Go-Interview vor? Das [Go Testing-Modul](/technologies/go/interview-questions/testing) behandelt diese Muster ausführlich mit Übungsfragen. ## Tests mit Coverage und Race Detection ausführen Der `go test`-Befehl akzeptiert Flags, die ungetestete Codepfade und Concurrency-Bugs aufdecken. ```bash # Tests mit Coverage-Bericht ausführen go test -cover ./... # HTML-Coverage-Visualisierung generieren go test -coverprofile=coverage.out ./... go tool cover -html=coverage.out -o coverage.html # Race Conditions erkennen (langsamer, aber findet echte Bugs) go test -race ./... ``` Der Race Detector instrumentiert Speicherzugriffe und meldet gleichzeitige Lese- und Schreibzugriffe auf dieselbe Variable. Die Ausführung von Tests mit `-race` in CI fängt Bugs ab, die sich nur unter bestimmtem Timing manifestieren. ## Tests in großen Codebasen organisieren Mit wachsenden Projekten beeinflusst die Testorganisation die Wartbarkeit: - **Same-Package-Tests** (`package foo`) haben Zugriff auf nicht-exportierte Funktionen und Felder. Für Unit-Tests verwenden, die internes Verhalten verifizieren. - **External-Package-Tests** (`package foo_test`) importieren das Paket wie externer Code. Für Integrationstests und zur Verifizierung der öffentlichen API verwenden. - **Testdata-Verzeichnisse** speichern Fixtures wie JSON-Dateien, Golden Outputs oder Seed-Datenbanken. Go ignoriert diese beim Build. - **Build-Tags** trennen Unit-Tests von Integrationstests, die externe Systeme erfordern. `go test -tags=integration ./...` ausführen, um sie einzubeziehen. Für mehr Go-Best-Practices über Testing hinaus behandelt der [Artikel über Concurrency-Muster](/blog/go/go-concurrency-goroutines-channels) Goroutines und Channels. ## Was man über Go-Testing wissen sollte - Das `testing`-Paket wird mit Go ausgeliefert und erfordert keine externen Abhängigkeiten - Tabellengesteuerte Tests reduzieren Duplikation und sind das erwartete Muster in Interviews - Interfaces ermöglichen Dependency Injection; sie am Verwendungsort definieren, nicht bei der Implementierung - `gomock` generiert Mocks aus Interfaces; `testify` bietet sauberere Assertions - `httptest.ResponseRecorder` erfasst Handler-Ausgaben ohne Netzwerk-Overhead - Das `-race`-Flag erkennt Concurrency-Bugs, die Unit-Tests sonst übersehen - Coverage-Metriken lenken die Aufmerksamkeit, garantieren aber keine Korrektheit --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/go/go-testing-2026-unit-tests-mocks-interview