Communitygithub.com

golang-testing

Go-Testmuster mit tabellengesteuerten Tests, Subtests, Benchmarks, Fuzzing und Testabdeckung. Folgt der TDD-Methodik mit idiomatischen Go-Praktiken. Verwenden Sie dies, wenn Sie Go-Tests schreiben – tabellengesteuerte Fälle, Subtests, Benchmarks, Fuzzing oder Coverage.

Was ist golang-testing?

Dieser Skill bietet eine umfassende Sammlung von Go-Testmustern, die sich an der Test-Driven Development (TDD)-Methodik orientieren. Er deckt essenzielle Go-Idiome ab, darunter tabellengesteuerte Tests für eine effiziente und vollständige Abdeckung, Subtests zur übersichtlichen Organisation zusammenhängender Tests und Benchmarks zur Leistungsbewertung. Darüber hinaus enthält der Skill Anleitungen zum Fuzzing für die Eingabevalidierung (ab Go 1.18), dem Einsatz von goldenen Dateien für Ausgabeerwartungen und der effektiven Messung der Testabdeckung. Für Entwickler, die mit Go-Tools wie dem `go test`-Befehl arbeiten ist dieser Skill als Nachschlagewerk und Werkzeugkasten konzipiert. Er bietet sofort einsetzbare Code-Vorlagen für übliche Testszenarien, motiviert zu sauberen, testbaren Designs durch den Einsatz von Interfaces für das Mocking und stellt eine klare, schrittweise TDD-Arbeitsanweisung („RED-GREEN-REFACTOR“) bereit, die den gesamten Entwicklungsprozess von der Definition des fehlschlagenden Tests bis hin zur abschließenden Implementierung und Optimierung begleitet.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/golang-testing

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Go-Testmuster

Umfassende Go-Testmuster für das Schreiben zuverlässiger, wartbarer Tests nach der TDD-Methodik.

Wann aktivieren

  • Beim Schreiben neuer Go-Funktionen oder -Methoden
  • Beim Hinzufügen von Testabdeckung zu vorhandenem Code
  • Beim Erstellen von Benchmarks für leistungskritischen Code
  • Beim Implementieren von Fuzz-Tests für die Eingabevalidierung
  • Beim Befolgen des TDD-Workflows in Go-Projekten

TDD-Workflow für Go

Der RED-GREEN-REFACTOR-Zyklus

ROT     → Schreiben Sie zuerst einen fehlschlagenden Test
GRÜN    → Schreiben Sie minimalen Code, um den Test zu bestehen
REFACTOR → Verbessern Sie den Code, während die Tests grün bleiben
WIEDERHOLEN → Fahren Sie mit der nächsten Anforderung fort

Schritt-für-Schritt-TDD in Go

// Schritt 1: Die Schnittstelle/Signatur definieren
// calculator.go
package calculator

func Add(a, b int) int {
    panic("nicht implementiert") // Platzhalter
}

// Schritt 2: Fehlschlagenden Test schreiben (ROT)
// calculator_test.go
package calculator

import "testing"

func TestAdd(t *testing.T) {
    got := Add(2, 3)
    want := 5
    if got != want {
        t.Errorf("Add(2, 3) = %d; want %d", got, want)
    }
}

// Schritt 3: Test ausführen – FEHLSCHLAG überprüfen
// $ go test
// --- FAIL: TestAdd (0.00s)
// panic: nicht implementiert

// Schritt 4: Minimalen Code implementieren (GRÜN)
func Add(a, b int) int {
    return a + b
}

// Schritt 5: Test ausführen – BESTANDEN überprüfen
// $ go test
// PASS

// Schritt 6: Bei Bedarf umgestalten, überprüfen, ob Tests weiterhin bestanden werden

Tabellengesteuerte Tests

Das Standardmuster für Go-Tests. Ermöglicht umfassende Abdeckung mit minimalem Code.

func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive Zahlen", 2, 3, 5},
        {"negative Zahlen", -1, -2, -3},
        {"Nullwerte", 0, 0, 0},
        {"gemischte Vorzeichen", -1, 1, 0},
        {"große Zahlen", 1000000, 2000000, 3000000},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := Add(tt.a, tt.b)
            if got != tt.expected {
                t.Errorf("Add(%d, %d) = %d; want %d",
                    tt.a, tt.b, got, tt.expected)
            }
        })
    }
}

Tabellengesteuerte Tests mit Fehlerfällen

func TestParseConfig(t *testing.T) {
    tests := []struct {
        name    string
        input   string
        want    *Config
        wantErr bool
    }{
        {
            name:  "gültige Konfiguration",
            input: `{"host": "localhost", "port": 8080}`,
            want:  &Config{Host: "localhost", Port: 8080},
        },
        {
            name:    "ungültiges JSON",
            input:   `{invalid}`,
            wantErr: true,
        },
        {
            name:    "leere Eingabe",
            input:   "",
            wantErr: true,
        },
        {
            name:  „minimale Konfiguration",
            input: `{}`,
            want:  &Config{}, // Konfiguration mit Nullwerten
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := ParseConfig(tt.input)

            if tt.wantErr {
                if err == nil {
                    t.Error("erwarteter Fehler, erhalten: nil")
                }
                return
            }

            if err != nil {
                t.Fatalf("unerwarteter Fehler: %v", err)
            }

            if !reflect.DeepEqual(got, tt.want) {
                t.Errorf("erhalten %+v; erwartet %+v", got, tt.want)
            }
        })
    }
}

Subtests und Sub-Benchmarks

Organisation verwandter Tests

func TestUser(t *testing.T) {
    // Von allen Subtests gemeinsam genutzte Einrichtung
    db := setupTestDB(t)

    t.Run("Erstellen", func(t *testing.T) {
        user := &User{Name: "Alice"}
        err := db.CreateUser(user)
        if err != nil {
            t.Fatalf("CreateUser fehlgeschlagen: %v", err)
        }
        if user.ID == "" {
            t.Error("erwartet, dass die Benutzer-ID gesetzt ist")
        }
    })

    t.Run("Abrufen", func(t *testing.T) {
        user, err := db.GetUser("alice-id")
        if err != nil {
            t.Fatalf("GetUser fehlgeschlagen: %v", err)
        }
        if user.Name != "Alice" {
            t.Errorf("erhaltener Name %q; erwartet %q", user.Name, "Alice")
        }
    })

    t.Run("Aktualisieren", func(t *testing.T) {
        // ...
    })

    t.Run("Löschen", func(t *testing.T) {
        // ...
    })
}

Parallele Subtests

func TestParallel(t *testing.T) {
    tests := []struct {
        name  string
        input string
    }{
        {"fall1", "eingabe1"},
        {"fall2", "eingabe2"},
        {"fall3", "eingabe3"},
    }

    for _, tt := range tests {
        tt := tt // Bereichsvariable erfassen
        t.Run(tt.name, func(t *testing.T) {
            t.Parallel() // Subtests parallel ausführen
            result := Process(tt.input)
            // Behauptungen...
            _ = result
        })
    }
}

Test-Helfer

Hilfsfunktionen

func setupTestDB(t *testing.T) *sql.DB {
    t.Helper() // Markiert dies als Hilfsfunktion

    db, err := sql.Open("sqlite3", ":memory:")
    if err != nil {
        t.Fatalf("Fehler beim Öffnen der Datenbank: %v", err)
    }

    // Bereinigung, wenn der Test abgeschlossen ist
    t.Cleanup(func() {
        db.Close()
    })

    // Migrationen ausführen
    if _, err := db.Exec(schema); err != nil {
        t.Fatalf("Fehler beim Erstellen des Schemas: %v", err)
    }

    return db
}

func assertNoError(t *testing.T, err error) {
    t.Helper()
    if err != nil {
        t.Fatalf("unerwarteter Fehler: %v", err)
    }
}

func assertEqual[T comparable](t *testing.T, got, want T) {
    t.Helper()
    if got != want {
        t.Errorf("erhalten %v; erwartet %v", got, want)
    }
}

Temporäre Dateien und Verzeichnisse

func TestFileProcessing(t *testing.T) {
    // Temporäres Verzeichnis erstellen – automatisch bereinigt
    tmpDir := t.TempDir()

    // Testdatei erstellen
    testFile := filepath.Join(tmpDir, "test.txt")
    err := os.WriteFile(testFile, []byte("Testinhalt"), 0644)
    if err != nil {
        t.Fatalf("Fehler beim Erstellen der Testdatei: %v", err)
    }

    // Test ausführen
    result, err := ProcessFile(testFile)
    if err != nil {
        t.Fatalf("ProcessFile fehlgeschlagen: %v", err)
    }

    // Behauptung...
    _ = result
}

Goldene Dateien

Testen gegen erwartete Ausgabedateien, die in testdata/ gespeichert sind.

var update = flag.Bool("update", false, "goldene Dateien aktualisieren")

func TestRender(t *testing.T) {
    tests := []struct {
        name  string
        input Template
    }{
        {"einfach", Template{Name: "test"}},
        {"komplex", Template{Name: "test", Items: []string{"a", "b"}}},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got := Render(tt.input)

            golden := filepath.Join("testdata", tt.name+".golden")

            if *update {
                // Goldene Datei aktualisieren: go test -update
                err := os.WriteFile(golden, got, 0644)
                if err != nil {
                    t.Fatalf("Fehler beim Aktualisieren der goldenen Datei: %v", err)
                }
            }

            want, err := os.ReadFile(golden)
            if err != nil {
                t.Fatalf("Fehler beim Lesen der goldenen Datei: %v", err)
            }

            if !bytes.Equal(got, want) {
                t.Errorf("Ausgabe stimmt nicht überein:\nerhalten:\n%s\nerwartet:\n%s", got, want)
            }
        })
    }
}

Mocken mit Interfaces

Interface-basiertes Mocken

// Interface für Abhängigkeiten definieren
type UserRepository interface {
    GetUser(id string) (*User, error)
    SaveUser(user *User) error
}

// Produktionsimplementierung
type PostgresUserRepository struct {
    db *sql.DB
}

func (r *PostgresUserRepository) GetUser(id string) (*User, error) {
    // Echte Datenbankabfrage
}

// Mock-Implementierung für Tests
type MockUserRepository struct {
    GetUserFunc  func(id string) (*User, error)
    SaveUserFunc func(user *User) error
}

func (m *MockUserRepository) GetUser(id string) (*User, error) {
    return m.GetUserFunc(id)
}

func (m *MockUserRepository) SaveUser(user *User) error {
    return m.SaveUserFunc(user)
}

// Test mit Mock
func TestUserService(t *testing.T) {
    mock := &MockUserRepository{
        GetUserFunc: func(id string) (*User, error) {
            if id == "123" {
                return &User{ID: "123", Name: "Alice"}, nil
            }
            return nil, ErrNotFound
        },
    }

    service := NewUserService(mock)

    user, err := service.GetUserProfile("123")
    if err != nil {
        t.Fatalf("unerwarteter Fehler: %v", err)
    }
    if user.Name != "Alice" {
        t.Errorf("erhaltener Name %q; erwartet %q", user.Name, "Alice")
    }
}

Benchmarks

Einfache Benchmarks

func BenchmarkProcess(b *testing.B) {
    data := generateTestData(1000)
    b.ResetTimer() // Einrichtungszeit nicht zählen

    for i := 0; i < b.N; i++ {
        Process(data)
    }
}

// Ausführen: go test -bench=BenchmarkProcess -benchmem
// Ausgabe: BenchmarkProcess-8   10000   105234 ns/op   4096 B/op   10 allocs/op

Benchmark mit verschiedenen Größen

func BenchmarkSort(b *testing.B) {
    sizes := []int{100, 1000, 10000, 100000}

    for _, size := range sizes {
        b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
            data := generateRandomSlice(size)
            b.ResetTimer()

            for i := 0; i < b.N; i++ {
                // Eine Kopie erstellen, um das Sortieren bereits sortierter Daten zu vermeiden
                tmp := make([]int, len(data))
                copy(tmp, data)
                sort.Ints(tmp)
            }
        })
    }
}

Benchmarks zur Speicherplatzzuweisung

func BenchmarkStringConcat(b *testing.B) {
    parts := []string{"hallo", "welt", "foo", "bar", "baz"}

    b.Run("plus", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            var s string
            for _, p := range parts {
                s += p
            }
            _ = s
        }
    })

    b.Run("builder", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            var sb strings.Builder
            for _, p := range parts {
                sb.WriteString(p)
            }
            _ = sb.String()
        }
    })

    b.Run("join", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            _ = strings.Join(parts, "")
        }
    })
}

Fuzzing (Go 1.18+)

Einfacher Fuzz-Test

func FuzzParseJSON(f *testing.F) {
    // Seed-Korpus hinzufügen
    f.Add(`{"name": "test"}`)
    f.Add(`{"count": 123}`)
    f.Add(`[]`)
    f.Add(`""`)

    f.Fuzz(func(t *testing.T, input string) {
        var result map[string]interface{}
        err := json.Unmarshal([]byte(input), &result)

        if err != nil {
            // Für zufällige Eingabe wird ungültiges JSON erwartet
            return
        }

        // Wenn das Parsen erfolgreich war, sollte die erneute Kodierung funktionieren
        _, err = json.Marshal(result)
        if err != nil {
            t.Errorf("Marshal fehlgeschlagen nach erfolgreichem Unmarshal: %v", err)
        }
    })
}

// Ausführen: go test -fuzz=FuzzParseJSON -fuzztime=30s

Fuzz-Test mit mehreren Eingaben

func FuzzCompare(f *testing.F) {
    f.Add("hallo", "welt")
    f.Add("", "")
    f.Add("abc", "abc")

    f.Fuzz(func(t *testing.T, a, b string) {
        result := Compare(a, b)

        // Eigenschaft: Compare(a, a) sollte immer 0 ergeben
        if a == b && result != 0 {
            t.Errorf("Compare(%q, %q) = %d; erwartet 0", a, b, result)
        }

        // Eigenschaft: Compare(a, b) und Compare(b, a) sollten entgegengesetzte Vorzeichen haben
        reverse := Compare(b, a)
        if (result > 0 && reverse >= 0) || (result < 0 && reverse <= 0) {
            if result != 0 || reverse != 0 {
                t.Errorf("Compare(%q, %q) = %d, Compare(%q, %q) = %d; inkonsistent",
                    a, b, result, b, a, reverse)
            }
        }
    })
}

Testabdeckung

Ausführen der Abdeckungsanalyse

# Einfache Abdeckung
go test -cover ./...

# Abdeckungsprofil generieren
go test -coverprofile=coverage.out ./...

# Abdeckung im Browser anzeigen
go tool cover -html=coverage.out

# Abdeckung nach Funktion anzeigen
go tool cover -func=coverage.out

# Abdeckung mit Race-Erkennung
go test -race -coverprofile=coverage.out ./...

Abdeckungsziele

Code-TypZiel
Kritische Geschäftslogik100%
Öffentliche APIs90%+
Allgemeiner Code80%+
Generierter CodeAusgeschlossen

Generierten Code von der Abdeckung ausschließen

//go:generate mockgen -source=interface.go -destination=mock_interface.go

// Im Abdeckungsprofil mit Build-Tags ausschließen:
// go test -cover -tags=!generate ./...

HTTP-Handler-Tests

func TestHealthHandler(t *testing.T) {
    // Anfrage erstellen
    req := httptest.NewRequest(http.MethodGet, "/health", nil)
    w := httptest.NewRecorder()

    // Handler aufrufen
    HealthHandler(w, req)

    // Antwort überprüfen
    resp := w.Result()
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        t.Errorf("erhaltener Status %d; erwartet %d", resp.StatusCode, http.StatusOK)
    }

    body, _ := io.ReadAll(resp.Body)
    if string(body) != "OK" {
        t.Errorf("erhaltener Inhalt %q; erwartet %q", body, "OK")
    }
}

func TestAPIHandler(t *testing.T) {
    tests := []struct {
        name       string
        method     string
        path       string
        body       string
        wantStatus int
        wantBody   string
    }{
        {
            name:       "Benutzer abrufen",
            method:     http.MethodGet,
            path:       "/users/123",
            wantStatus: http.StatusOK,
            wantBody:   `{"id":"123","name":"Alice"}`,
        },
        {
            name:       "nicht gefunden",
            method:     http.MethodGet,
            path:       "/users/999",
            wantStatus: http.StatusNotFound,
        },
        {
            name:       "Benutzer erstellen",
            method:     http.MethodPost,
            path:       "/users",
            body:       `{"name":"Bob"}`,
            wantStatus: http.StatusCreated,
        },
    }

    handler := NewAPIHandler()

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            // ...
        })
    }
}

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Verwandte Skills