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.

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.
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.
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 und in Produktionscodebases der gesamten Branche.
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.
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-Paket generiert Mock-Implementierungen aus Interface-Definitionen. Der mockgen-Befehl liest Quelldateien und erzeugt Mock-Structs, die Aufrufe aufzeichnen und konfigurierte Werte zurückgeben.
# Mocks für das Repository-Interface generieren
mockgen -source=service.go -destination=mocks/repository_mock.go -package=mocksVerwendung des generierten Mocks in Tests:
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 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.
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.
Bereit für deine Go-Interviews?
Übe mit unseren interaktiven Simulatoren, Flashcards und technischen Tests.
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.
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 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.
# 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 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
gomockgeneriert Mocks aus Interfaces;testifybietet sauberere Assertionshttptest.ResponseRecordererfasst 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
Fang an zu üben!
Teste dein Wissen mit unseren Interview-Simulatoren und technischen Tests.
Findest du den Bug in Go?
Ein echter Codeausschnitt, ein versteckter Bug, ein Versuch pro Tag. Zum Ausprobieren ohne Konto.

Geschrieben von
Anthony Fillion-MailletGründer von SharpSkill
Seit über 10 Jahren Fullstack-Entwickler. Er leitet SharpSkill und verantwortet alles, was hier erscheint.
Aktualisiert am 24. August 2026
Tags
Teilen
Verwandte Artikel

Go Fehlerbehandlung 2026: Patterns, Wrapping und technische Interviewfragen
Fehlerbehandlung in Go unterscheidet sich grundlegend von anderen Sprachen. Dieser Artikel behandelt Error Wrapping, Sentinel Errors und typische Interviewfragen.

Go Design Patterns: Die wichtigsten Muster und Interview-Fragen fuer Go-Entwickler
Die sechs wichtigsten Go Design Patterns mit produktionsreifem Code: Functional Options, Strategy, Factory, Observer, Middleware und Struct Embedding. Inklusive typischer Interview-Fragen.

Fortgeschrittene Go-Interfaces 2026: Komposition, Type Assertions und Interview-Fragen
Ein umfassender Leitfaden zu Go-Interfaces: Komposition durch Embedding, sichere Type Assertions, selbstreferenzielle Generics in Go 1.26 und die häufigsten Interview-Fragen für Senior-Entwickler.