Golang Testing

affaan-m/ECC/.kiro/skills/golang-testing

作者 affaan-mef648e01899ba3e8dc6371642deaaf64b4477775無授權條款275K 個星標收錄於 2026年10月9日更新於 2026年10月9日儲存庫4 天前更新

Go testing best practices including table-driven tests, test helpers, benchmarking, race detection, coverage analysis, and integration testing patterns. Use when writing or improving Go tests.

AI 產生的概覽

提供 Go 測試實務指引:表格驅動測試、輔助函式、競態偵測、覆蓋率、基準測試與整合測試。

功能
此技能提供 Go 專屬的測試指引,涵蓋表格驅動測試、測試輔助函式、測試 fixture 與清理、競態偵測、覆蓋率分析、基準測試、模擬以及整合測試。它提供程式碼範例與指令,例如 go test -race、覆蓋率分析檔與建置標籤。它只是純說明性參考,本身不會產生檔案或指令碼。
適用情境
適用於撰寫或改善 Go 測試、提升測試覆蓋率、建置測試基礎設施、排查不穩定測試或加入整合測試的情境。也適合最佳化測試效能與採用道地的 Go 測試慣例。
執行需求
需要 Go 工具鏈來執行所述指令;部分範例涉及 sqlite3、testcontainers 與 benchstat。此技能未隨附指令碼。

Go Testing

This skill provides comprehensive Go testing patterns extending common testing principles with Go-specific idioms.

Testing Framework

Use the standard go test with table-driven tests as the primary pattern.

Table-Driven Tests

The idiomatic Go testing pattern:

go
func TestValidateEmail(t *testing.T) {    tests := []struct {        name    string        email   string        wantErr bool    }{        {            name:    "valid email",            email:   "[email protected]",            wantErr: false,        },        {            name:    "missing @",            email:   "userexample.com",            wantErr: true,        },        {            name:    "empty string",            email:   "",            wantErr: true,        },    }
    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)            }        })    }}

Benefits:

  • Easy to add new test cases
  • Clear test case documentation
  • Parallel test execution with t.Parallel()
  • Isolated subtests with t.Run()

Test Helpers

Use t.Helper() to mark helper functions:

go
func assertNoError(t *testing.T, err error) {    t.Helper()    if err != nil {        t.Fatalf("unexpected error: %v", err)    }}
func assertEqual(t *testing.T, got, want interface{}) {    t.Helper()    if !reflect.DeepEqual(got, want) {        t.Errorf("got %v, want %v", got, want)    }}

Benefits:

  • Correct line numbers in test failures
  • Reusable test utilities
  • Cleaner test code

Test Fixtures

Use t.Cleanup() for resource cleanup:

go
func testDB(t *testing.T) *sql.DB {    t.Helper()
    db, err := sql.Open("sqlite3", ":memory:")    if err != nil {        t.Fatalf("failed to open test db: %v", err)    }
    // Cleanup runs after test completes    t.Cleanup(func() {        if err := db.Close(); err != nil {            t.Errorf("failed to close db: %v", err)        }    })
    return db}
func TestUserRepository(t *testing.T) {    db := testDB(t)    repo := NewUserRepository(db)    // ... test logic}

Race Detection

Always run tests with the -race flag to detect data races:

bash
go test -race ./...

In CI/CD:

yaml
- name: Test with race detector  run: go test -race -timeout 5m ./...

Why:

  • Detects concurrent access bugs
  • Prevents production race conditions
  • Minimal performance overhead in tests

Coverage Analysis

Basic Coverage

bash
go test -cover ./...

Detailed Coverage Report

bash
go test -coverprofile=coverage.out ./...go tool cover -html=coverage.out

Coverage Thresholds

bash
# Fail if coverage below 80%go test -cover ./... | grep -E 'coverage: [0-7][0-9]\.[0-9]%' && exit 1

Benchmarking

go
func BenchmarkValidateEmail(b *testing.B) {    email := "[email protected]"
    b.ResetTimer()    for i := 0; i < b.N; i++ {        ValidateEmail(email)    }}

Run benchmarks:

bash
go test -bench=. -benchmem

Compare benchmarks:

bash
go test -bench=. -benchmem > old.txt# make changesgo test -bench=. -benchmem > new.txtbenchstat old.txt new.txt

Mocking

Interface-Based Mocking

go
type UserRepository interface {    GetUser(id string) (*User, error)}
type mockUserRepository struct {    users map[string]*User    err   error}
func (m *mockUserRepository) GetUser(id string) (*User, error) {    if m.err != nil {        return nil, m.err    }    return m.users[id], nil}
func TestUserService(t *testing.T) {    mock := &mockUserRepository{        users: map[string]*User{            "1": {ID: "1", Name: "Alice"},        },    }
    service := NewUserService(mock)    // ... test logic}

Integration Tests

Build Tags

go
//go:build integration// +build integration
package user_test
func TestUserRepository_Integration(t *testing.T) {    // ... integration test}

Run integration tests:

bash
go test -tags=integration ./...

Test Containers

go
func TestWithPostgres(t *testing.T) {    if testing.Short() {        t.Skip("skipping integration test")    }
    // Setup test container    ctx := context.Background()    container, err := testcontainers.GenericContainer(ctx, ...)    assertNoError(t, err)
    t.Cleanup(func() {        container.Terminate(ctx)    })
    // ... test logic}

Test Organization

File Structure

package/├── user.go├── user_test.go          # Unit tests├── user_integration_test.go  # Integration tests└── testdata/             # Test fixtures    └── users.json

Package Naming

go
// Black-box testing (external perspective)package user_test
// White-box testing (internal access)package user

Common Patterns

Testing HTTP Handlers

go
func TestUserHandler(t *testing.T) {    req := httptest.NewRequest("GET", "/users/1", nil)    rec := httptest.NewRecorder()
    handler := NewUserHandler(mockRepo)    handler.ServeHTTP(rec, req)
    assertEqual(t, rec.Code, http.StatusOK)}

Testing with Context

go
func TestWithTimeout(t *testing.T) {    ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)    defer cancel()
    err := SlowOperation(ctx)    if !errors.Is(err, context.DeadlineExceeded) {        t.Errorf("expected timeout error, got %v", err)    }}

Best Practices

  1. Use t.Parallel() for independent tests
  2. Use testing.Short() to skip slow tests
  3. Use t.TempDir() for temporary directories
  4. Use t.Setenv() for environment variables
  5. Avoid init() in test files
  6. Keep tests focused - one behavior per test
  7. Use meaningful test names - describe what's being tested

When to Use This Skill

  • Writing new Go tests
  • Improving test coverage
  • Setting up test infrastructure
  • Debugging flaky tests
  • Optimizing test performance
  • Implementing integration tests

來源與署名

來源:affaan-m/ECC位於.kiro/skills/golang-testing提交ef648e0

授權條款: 無授權條款

內容歸原作者所有。SourceWeft 從公開儲存庫中收錄這些內容。

檢舉或申請下架