Source profileQuality 87/100Review permissions

affaan-m/ECC/skills/golang-testing/SKILL.md

golang-testing

Go testing patterns including table-driven tests, subtests, benchmarks, fuzzing, and test coverage. Follows TDD methodology with idiomatic Go practices.

Source repository stars
234,327
Declared platforms
0
Static risk flags
3
Last source update
2026-07-27
Source checked
2026-07-28

Decision brief

What it does—and where it fits

Comprehensive Go testing patterns for writing reliable, maintainable tests following TDD methodology.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/affaan-m/ECC --skill "skills/golang-testing"
    Safe inspection promptEditorial

    Inspect the Agent Skill "golang-testing" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/skills/golang-testing/SKILL.md at commit 4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      TDD Workflow for Go

      Review the “TDD Workflow for Go” section in the pinned source before continuing.

      Review and apply the “TDD Workflow for Go” source section.
    2. 02

      Step-by-Step TDD in Go

      Review the “Step-by-Step TDD in Go” section in the pinned source before continuing.

      Review and apply the “Step-by-Step TDD in Go” source section.
    3. 03

      When to Activate

      Writing new Go functions or methods

      Writing new Go functions or methodsAdding test coverage to existing codeCreating benchmarks for performance-critical code
    4. 04

      The RED-GREEN-REFACTOR Cycle

      Review the “The RED-GREEN-REFACTOR Cycle” section in the pinned source before continuing.

      Review and apply the “The RED-GREEN-REFACTOR Cycle” source section.
    5. 05

      Table-Driven Tests

      The standard pattern for Go tests. Enables comprehensive coverage with minimal code.

      The standard pattern for Go tests. Enables comprehensive coverage with minimal code.

    Permission review

    Static risk signals and limitations

    Writes files

    medium · line 261

    The documentation asks the agent to create, modify, or delete local files.

    // Create temp directory - automatically cleaned up

    Writes files

    medium · line 264

    The documentation asks the agent to create, modify, or delete local files.

    // Create test file

    Reads files

    low · line 312

    The documentation asks the agent to read local files, directories, or repositories.

    want, err := os.ReadFile(golden)

    Reads files

    low · line 314

    The documentation asks the agent to read local files, directories, or repositories.

    t.Fatalf("failed to read golden file: %v", err)

    Runs scripts

    medium · line 522

    The documentation asks the agent to run terminal commands or scripts.

    go test -cover ./...

    Runs scripts

    medium · line 525

    The documentation asks the agent to run terminal commands or scripts.

    go test -coverprofile=coverage.out ./...

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score87/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars234,327SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    affaan-m/ECC
    Skill path
    skills/golang-testing/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    Go Testing Patterns

    Comprehensive Go testing patterns for writing reliable, maintainable tests following TDD methodology.

    When to Activate

    • Writing new Go functions or methods
    • Adding test coverage to existing code
    • Creating benchmarks for performance-critical code
    • Implementing fuzz tests for input validation
    • Following TDD workflow in Go projects

    TDD Workflow for Go

    The RED-GREEN-REFACTOR Cycle

    RED     → Write a failing test first
    GREEN   → Write minimal code to pass the test
    REFACTOR → Improve code while keeping tests green
    REPEAT  → Continue with next requirement
    

    Step-by-Step TDD in Go

    // Step 1: Define the interface/signature
    // calculator.go
    package calculator
    
    func Add(a, b int) int {
        panic("not implemented") // Placeholder
    }
    
    // Step 2: Write failing test (RED)
    // 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)
        }
    }
    
    // Step 3: Run test - verify FAIL
    // $ go test
    // --- FAIL: TestAdd (0.00s)
    // panic: not implemented
    
    // Step 4: Implement minimal code (GREEN)
    func Add(a, b int) int {
        return a + b
    }
    
    // Step 5: Run test - verify PASS
    // $ go test
    // PASS
    
    // Step 6: Refactor if needed, verify tests still pass
    

    Table-Driven Tests

    The standard pattern for Go tests. Enables comprehensive coverage with minimal code.

    func TestAdd(t *testing.T) {
        tests := []struct {
            name     string
            a, b     int
            expected int
        }{
            {"positive numbers", 2, 3, 5},
            {"negative numbers", -1, -2, -3},
            {"zero values", 0, 0, 0},
            {"mixed signs", -1, 1, 0},
            {"large numbers", 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)
                }
            })
        }
    }
    

    Table-Driven Tests with Error Cases

    func TestParseConfig(t *testing.T) {
        tests := []struct {
            name    string
            input   string
            want    *Config
            wantErr bool
        }{
            {
                name:  "valid config",
                input: `{"host": "localhost", "port": 8080}`,
                want:  &Config{Host: "localhost", Port: 8080},
            },
            {
                name:    "invalid JSON",
                input:   `{invalid}`,
                wantErr: true,
            },
            {
                name:    "empty input",
                input:   "",
                wantErr: true,
            },
            {
                name:  "minimal config",
                input: `{}`,
                want:  &Config{}, // Zero value config
            },
        }
    
        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("expected error, got nil")
                    }
                    return
                }
    
                if err != nil {
                    t.Fatalf("unexpected error: %v", err)
                }
    
                if !reflect.DeepEqual(got, tt.want) {
                    t.Errorf("got %+v; want %+v", got, tt.want)
                }
            })
        }
    }
    

    Subtests and Sub-benchmarks

    Organizing Related Tests

    func TestUser(t *testing.T) {
        // Setup shared by all subtests
        db := setupTestDB(t)
    
        t.Run("Create", func(t *testing.T) {
            user := &User{Name: "Alice"}
            err := db.CreateUser(user)
            if err != nil {
                t.Fatalf("CreateUser failed: %v", err)
            }
            if user.ID == "" {
                t.Error("expected user ID to be set")
            }
        })
    
        t.Run("Get", func(t *testing.T) {
            user, err := db.GetUser("alice-id")
            if err != nil {
                t.Fatalf("GetUser failed: %v", err)
            }
            if user.Name != "Alice" {
                t.Errorf("got name %q; want %q", user.Name, "Alice")
            }
        })
    
        t.Run("Update", func(t *testing.T) {
            // ...
        })
    
        t.Run("Delete", func(t *testing.T) {
            // ...
        })
    }
    

    Parallel Subtests

    func TestParallel(t *testing.T) {
        tests := []struct {
            name  string
            input string
        }{
            {"case1", "input1"},
            {"case2", "input2"},
            {"case3", "input3"},
        }
    
        for _, tt := range tests {
            tt := tt // Capture range variable
            t.Run(tt.name, func(t *testing.T) {
                t.Parallel() // Run subtests in parallel
                result := Process(tt.input)
                // assertions...
                _ = result
            })
        }
    }
    

    Test Helpers

    Helper Functions

    func setupTestDB(t *testing.T) *sql.DB {
        t.Helper() // Marks this as a helper function
    
        db, err := sql.Open("sqlite3", ":memory:")
        if err != nil {
            t.Fatalf("failed to open database: %v", err)
        }
    
        // Cleanup when test finishes
        t.Cleanup(func() {
            db.Close()
        })
    
        // Run migrations
        if _, err := db.Exec(schema); err != nil {
            t.Fatalf("failed to create schema: %v", err)
        }
    
        return db
    }
    
    func assertNoError(t *testing.T, err error) {
        t.Helper()
        if err != nil {
            t.Fatalf("unexpected error: %v", err)
        }
    }
    
    func assertEqual[T comparable](t *testing.T, got, want T) {
        t.Helper()
        if got != want {
            t.Errorf("got %v; want %v", got, want)
        }
    }
    

    Temporary Files and Directories

    func TestFileProcessing(t *testing.T) {
        // Create temp directory - automatically cleaned up
        tmpDir := t.TempDir()
    
        // Create test file
        testFile := filepath.Join(tmpDir, "test.txt")
        err := os.WriteFile(testFile, []byte("test content"), 0644)
        if err != nil {
            t.Fatalf("failed to create test file: %v", err)
        }
    
        // Run test
        result, err := ProcessFile(testFile)
        if err != nil {
            t.Fatalf("ProcessFile failed: %v", err)
        }
    
        // Assert...
        _ = result
    }
    

    Golden Files

    Testing against expected output files stored in testdata/.

    var update = flag.Bool("update", false, "update golden files")
    
    func TestRender(t *testing.T) {
        tests := []struct {
            name  string
            input Template
        }{
            {"simple", Template{Name: "test"}},
            {"complex", 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 {
                    // Update golden file: go test -update
                    err := os.WriteFile(golden, got, 0644)
                    if err != nil {
                        t.Fatalf("failed to update golden file: %v", err)
                    }
                }
    
                want, err := os.ReadFile(golden)
                if err != nil {
                    t.Fatalf("failed to read golden file: %v", err)
                }
    
                if !bytes.Equal(got, want) {
                    t.Errorf("output mismatch:\ngot:\n%s\nwant:\n%s", got, want)
                }
            })
        }
    }
    

    Mocking with Interfaces

    Interface-Based Mocking

    // Define interface for dependencies
    type UserRepository interface {
        GetUser(id string) (*User, error)
        SaveUser(user *User) error
    }
    
    // Production implementation
    type PostgresUserRepository struct {
        db *sql.DB
    }
    
    func (r *PostgresUserRepository) GetUser(id string) (*User, error) {
        // Real database query
    }
    
    // Mock implementation for 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 using 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("unexpected error: %v", err)
        }
        if user.Name != "Alice" {
            t.Errorf("got name %q; want %q", user.Name, "Alice")
        }
    }
    

    Benchmarks

    Basic Benchmarks

    func BenchmarkProcess(b *testing.B) {
        data := generateTestData(1000)
        b.ResetTimer() // Don't count setup time
    
        for i := 0; i < b.N; i++ {
            Process(data)
        }
    }
    
    // Run: go test -bench=BenchmarkProcess -benchmem
    // Output: BenchmarkProcess-8   10000   105234 ns/op   4096 B/op   10 allocs/op
    

    Benchmark with Different Sizes

    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++ {
                    // Make a copy to avoid sorting already sorted data
                    tmp := make([]int, len(data))
                    copy(tmp, data)
                    sort.Ints(tmp)
                }
            })
        }
    }
    

    Memory Allocation Benchmarks

    func BenchmarkStringConcat(b *testing.B) {
        parts := []string{"hello", "world", "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+)

    Basic Fuzz Test

    func FuzzParseJSON(f *testing.F) {
        // Add seed corpus
        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 {
                // Invalid JSON is expected for random input
                return
            }
    
            // If parsing succeeded, re-encoding should work
            _, err = json.Marshal(result)
            if err != nil {
                t.Errorf("Marshal failed after successful Unmarshal: %v", err)
            }
        })
    }
    
    // Run: go test -fuzz=FuzzParseJSON -fuzztime=30s
    

    Fuzz Test with Multiple Inputs

    func FuzzCompare(f *testing.F) {
        f.Add("hello", "world")
        f.Add("", "")
        f.Add("abc", "abc")
    
        f.Fuzz(func(t *testing.T, a, b string) {
            result := Compare(a, b)
    
            // Property: Compare(a, a) should always equal 0
            if a == b && result != 0 {
                t.Errorf("Compare(%q, %q) = %d; want 0", a, b, result)
            }
    
            // Property: Compare(a, b) and Compare(b, a) should have opposite signs
            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; inconsistent",
                        a, b, result, b, a, reverse)
                }
            }
        })
    }
    

    Test Coverage

    Running Coverage

    # Basic coverage
    go test -cover ./...
    
    # Generate coverage profile
    go test -coverprofile=coverage.out ./...
    
    # View coverage in browser
    go tool cover -html=coverage.out
    
    # View coverage by function
    go tool cover -func=coverage.out
    
    # Coverage with race detection
    go test -race -coverprofile=coverage.out ./...
    

    Coverage Targets

    Code TypeTarget
    Critical business logic100%
    Public APIs90%+
    General code80%+
    Generated codeExclude

    Excluding Generated Code from Coverage

    //go:generate mockgen -source=interface.go -destination=mock_interface.go
    
    // In coverage profile, exclude with build tags:
    // go test -cover -tags=!generate ./...
    

    HTTP Handler Testing

    func TestHealthHandler(t *testing.T) {
        // Create request
        req := httptest.NewRequest(http.MethodGet, "/health", nil)
        w := httptest.NewRecorder()
    
        // Call handler
        HealthHandler(w, req)
    
        // Check response
        resp := w.Result()
        defer resp.Body.Close()
    
        if resp.StatusCode != http.StatusOK {
            t.Errorf("got status %d; want %d", resp.StatusCode, http.StatusOK)
        }
    
        body, _ := io.ReadAll(resp.Body)
        if string(body) != "OK" {
            t.Errorf("got body %q; want %q", body, "OK")
        }
    }
    
    func TestAPIHandler(t *testing.T) {
        tests := []struct {
            name       string
            method     string
            path       string
            body       string
            wantStatus int
            wantBody   string
        }{
            {
                name:       "get user",
                method:     http.MethodGet,
                path:       "/users/123",
                wantStatus: http.StatusOK,
                wantBody:   `{"id":"123","name":"Alice"}`,
            },
            {
                name:       "not found",
                method:     http.MethodGet,
                path:       "/users/999",
                wantStatus: http.StatusNotFound,
            },
            {
                name:       "create user",
                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) {
                var body io.Reader
                if tt.body != "" {
                    body = strings.NewReader(tt.body)
                }
    
                req := httptest.NewRequest(tt.method, tt.path, body)
                req.Header.Set("Content-Type", "application/json")
                w := httptest.NewRecorder()
    
                handler.ServeHTTP(w, req)
    
                if w.Code != tt.wantStatus {
                    t.Errorf("got status %d; want %d", w.Code, tt.wantStatus)
                }
    
                if tt.wantBody != "" && w.Body.String() != tt.wantBody {
                    t.Errorf("got body %q; want %q", w.Body.String(), tt.wantBody)
                }
            })
        }
    }
    

    Testing Commands

    # Run all tests
    go test ./...
    
    # Run tests with verbose output
    go test -v ./...
    
    # Run specific test
    go test -run TestAdd ./...
    
    # Run tests matching pattern
    go test -run "TestUser/Create" ./...
    
    # Run tests with race detector
    go test -race ./...
    
    # Run tests with coverage
    go test -cover -coverprofile=coverage.out ./...
    
    # Run short tests only
    go test -short ./...
    
    # Run tests with timeout
    go test -timeout 30s ./...
    
    # Run benchmarks
    go test -bench=. -benchmem ./...
    
    # Run fuzzing
    go test -fuzz=FuzzParse -fuzztime=30s ./...
    
    # Count test runs (for flaky test detection)
    go test -count=10 ./...
    

    Best Practices

    DO:

    • Write tests FIRST (TDD)
    • Use table-driven tests for comprehensive coverage
    • Test behavior, not implementation
    • Use t.Helper() in helper functions
    • Use t.Parallel() for independent tests
    • Clean up resources with t.Cleanup()
    • Use meaningful test names that describe the scenario

    DON'T:

    • Test private functions directly (test through public API)
    • Use time.Sleep() in tests (use channels or conditions)
    • Ignore flaky tests (fix or remove them)
    • Mock everything (prefer integration tests when possible)
    • Skip error path testing

    Integration with CI/CD

    # GitHub Actions example
    test:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v4
        - uses: actions/setup-go@v5
          with:
            go-version: '1.22'
    
        - name: Run tests
          run: go test -race -coverprofile=coverage.out ./...
    
        - name: Check coverage
          run: |
            go tool cover -func=coverage.out | grep total | awk '{print $3}' | \
            awk -F'%' '{if ($1 < 80) exit 1}'
    

    Remember: Tests are documentation. They show how your code is meant to be used. Write them clearly and keep them up to date.

    Alternatives

    Compare before choosing