Source profileQuality 80/100Review permissions

affaan-m/ECC/docs/zh-CN/skills/golang-testing/SKILL.md

golang-testing

Use it for testing and engineering tasks; the detail page covers purpose, installation, and practical steps.

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

遵循 TDD 方法论,用于编写可靠、可维护测试的全面 Go 测试模式。

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 "docs/zh-CN/skills/golang-testing"
    Safe inspection promptEditorial

    Inspect the Agent Skill "golang-testing" from https://github.com/affaan-m/ECC/blob/4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38/docs/zh-CN/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

      何时激活

      编写新的 Go 函数或方法时 为现有代码添加测试覆盖率时 为性能关键代码创建基准测试时 为输入验证实现模糊测试时 在 Go 项目中遵循 TDD 工作流时

      编写新的 Go 函数或方法时为现有代码添加测试覆盖率时为性能关键代码创建基准测试时
    2. 02

      Go 的 TDD 工作流

      Review the “Go 的 TDD 工作流” section in the pinned source before continuing.

      Review and apply the “Go 的 TDD 工作流” source section.
    3. 03

      红-绿-重构循环

      Review the “红-绿-重构循环” section in the pinned source before continuing.

      Review and apply the “红-绿-重构循环” source section.
    4. 04

      Go 中的分步 TDD

      Review the “Go 中的分步 TDD” section in the pinned source before continuing.

      Review and apply the “Go 中的分步 TDD” source section.
    5. 05

      表驱动测试

      Go 测试的标准模式。以最少的代码实现全面的覆盖。

      Go 测试的标准模式。以最少的代码实现全面的覆盖。

    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 score80/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
    docs/zh-CN/skills/golang-testing/SKILL.md
    Commit
    4e973d3eaf92d97f8d2e2d8abb39d8bdc8711b38
    License
    MIT
    Collected
    2026-07-28
    Default branch
    main
    View the original SKILL.md

    Go 测试模式

    遵循 TDD 方法论,用于编写可靠、可维护测试的全面 Go 测试模式。

    何时激活

    • 编写新的 Go 函数或方法时
    • 为现有代码添加测试覆盖率时
    • 为性能关键代码创建基准测试时
    • 为输入验证实现模糊测试时
    • 在 Go 项目中遵循 TDD 工作流时

    Go 的 TDD 工作流

    红-绿-重构循环

    RED     → 首先编写一个失败的测试
    GREEN   → 编写最少的代码来通过测试
    REFACTOR → 改进代码,同时保持测试通过
    REPEAT  → 继续处理下一个需求
    

    Go 中的分步 TDD

    // 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
    

    表驱动测试

    Go 测试的标准模式。以最少的代码实现全面的覆盖。

    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)
                }
            })
        }
    }
    

    包含错误情况的表驱动测试

    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)
                }
            })
        }
    }
    

    子测试和子基准测试

    组织相关测试

    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) {
            // ...
        })
    }
    

    并行子测试

    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
            })
        }
    }
    

    测试辅助函数

    辅助函数

    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)
        }
    }
    

    临时文件和目录

    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
    }
    

    黄金文件

    针对存储在 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)
                }
            })
        }
    }
    

    使用接口进行模拟

    基于接口的模拟

    // 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")
        }
    }
    

    基准测试

    基本基准测试

    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
    

    不同大小的基准测试

    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)
                }
            })
        }
    }
    

    内存分配基准测试

    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, "")
            }
        })
    }
    

    模糊测试 (Go 1.18+)

    基本模糊测试

    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
    

    多输入模糊测试

    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)
                }
            }
        })
    }
    

    测试覆盖率

    运行覆盖率

    # 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 ./...
    

    覆盖率目标

    代码类型目标
    关键业务逻辑100%
    公共 API90%+
    通用代码80%+
    生成的代码排除

    从覆盖率中排除生成的代码

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

    HTTP 处理器测试

    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)
                }
            })
        }
    }
    

    命令测试

    # 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 ./...
    

    最佳实践

    应该:

    • 写测试 (TDD)
    • 使用表驱动测试以实现全面覆盖
    • 测试行为,而非实现
    • 在辅助函数中使用 t.Helper()
    • 对于独立的测试使用 t.Parallel()
    • 使用 t.Cleanup() 清理资源
    • 使用描述场景的有意义的测试名称

    不应该:

    • 直接测试私有函数 (通过公共 API 测试)
    • 在测试中使用 time.Sleep() (使用通道或条件)
    • 忽略不稳定的测试 (修复或移除它们)
    • 模拟所有东西 (在可能的情况下优先使用集成测试)
    • 跳过错误路径测试

    与 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}'
    

    记住:测试即文档。它们展示了你的代码应如何使用。清晰地编写它们并保持更新。

    Alternatives

    Compare before choosing