Skip to content
清晨的一缕阳光
返回

Go 代码规范与测试

Go 代码规范与测试

Go 内置了完整的测试工具链,理解并正确使用它们能提高代码质量。

一、代码规范

1.1 go fmt

# 格式化代码
go fmt ./...

# 检查格式
gofmt -l .

1.2 go vet

# 静态检查
go vet ./...

# 检查特定问题
go vet -printf ./...
go vet -shadow ./...

1.3 golangci-lint

# 安装
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest

# 运行
golangci-lint run

# 配置文件
# .golangci.yml
linters:
  enable:
    - gofmt
    - govet
    - staticcheck

二、单元测试

2.1 基础测试

// calculator.go
func Add(a, b int) int {
    return a + b
}

// calculator_test.go
func TestAdd(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive", 1, 2, 3},
        {"negative", -1, -2, -3},
        {"zero", 0, 0, 0},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Add(%d, %d) = %d, want %d", 
                    tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

2.2 表驱动测试

func TestCalculator(t *testing.T) {
    tests := []struct {
        name     string
        fn       func(int, int) int
        a, b     int
        expected int
    }{
        {"Add", Add, 1, 2, 3},
        {"Sub", Sub, 5, 3, 2},
        {"Mul", Mul, 2, 3, 6},
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := tt.fn(tt.a, tt.b); got != tt.expected {
                t.Errorf("%s(%d, %d) = %d, want %d",
                    tt.name, tt.a, tt.b, got, tt.expected)
            }
        })
    }
}

2.3 测试辅助函数

// 测试辅助
func TestMain(m *testing.M) {
    // 设置
    setup()
    
    // 运行测试
    code := m.Run()
    
    // 清理
    teardown()
    
    os.Exit(code)
}

func setup() {
    // 初始化测试环境
}

func teardown() {
    // 清理测试环境
}

三、基准测试

3.1 基础基准测试

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(1, 2)
    }
}

// 运行
// go test -bench=. -benchmem

3.2 对比基准测试

func BenchmarkAddNaive(b *testing.B) {
    for i := 0; i < b.N; i++ {
        result := 0
        for j := 0; j < 1000; j++ {
            result += j
        }
    }
}

func BenchmarkAddOptimized(b *testing.B) {
    for i := 0; i < b.N; i++ {
        // 使用公式优化
        result := 999 * 1000 / 2
    }
}

// 对比结果
// BenchmarkAddNaive-8       100000    12000 ns/op
// BenchmarkAddOptimized-8   1000000000    0.5 ns/op

3.3 内存分配分析

func BenchmarkAlloc(b *testing.B) {
    b.ReportAllocs()
    
    for i := 0; i < b.N; i++ {
        // 测试内存分配
        s := make([]int, 100)
        _ = s
    }
}

// 输出
// BenchmarkAlloc-8    100000    1200 ns/op    800 B/op    1 allocs/op

四、模糊测试

4.1 基础模糊测试

func FuzzAdd(f *testing.F) {
    // 添加种子语料
    f.Add(1, 2)
    f.Add(-1, 1)
    f.Add(0, 0)
    
    // 模糊测试函数
    f.Fuzz(func(t *testing.T, a, b int) {
        result := Add(a, b)
        
        // 验证不变性
        if Add(b, a) != result {
            t.Errorf("Add(%d, %d) != Add(%d, %d)", a, b, b, a)
        }
    })
}

// 运行
// go test -fuzz=FuzzAdd

4.2 模糊测试技巧

func FuzzParse(f *testing.F) {
    f.Add("valid input")
    f.Add("")
    f.Add("特殊字符!@#$%")
    
    f.Fuzz(func(t *testing.T, input string) {
        result, err := Parse(input)
        
        if err == nil {
            // 如果解析成功,验证结果
            if result == nil {
                t.Error("Parse succeeded but returned nil")
            }
        }
        // 错误是允许的
    })
}

五、测试覆盖率

5.1 覆盖率报告

# 生成覆盖率
go test -coverprofile=cover.out ./...

# 查看覆盖率
go tool cover -func=cover.out

# HTML 报告
go tool cover -html=cover.out

5.2 覆盖率目标

# 设置覆盖率目标
go test -coverprofile=cover.out -covermode=count ./...

# 检查是否达到目标
go tool cover -func=cover.out | grep total | awk '{if ($3 < 80.0) exit 1}'

六、最佳实践

6.1 测试命名

// ✅ 推荐
func TestAdd(t *testing.T)
func TestAdd_Positive(t *testing.T)
func TestAdd_Negative(t *testing.T)
func BenchmarkAdd(b *testing.B)
func FuzzAdd(f *testing.F)

// ❌ 避免
func Test(t *testing.T)
func Test1(t *testing.T)

6.2 测试独立性

// ✅ 推荐:每个测试独立
func TestA(t *testing.T) {
    // 不依赖其他测试
}

func TestB(t *testing.T) {
    // 不依赖其他测试
}

// ❌ 避免:测试间依赖
func TestA(t *testing.T) {
    // 设置状态
}

func TestB(t *testing.T) {
    // 依赖 TestA 的状态
}

6.3 并行测试

func TestParallel(t *testing.T) {
    t.Parallel()
    
    // 可以并行执行的测试
}

// 运行并行测试
// go test -parallel 4

七、总结

Go 测试核心要点:

工具用途命令
go fmt代码格式化go fmt
go vet静态检查go vet
单元测试功能验证go test
基准测试性能测试go test -bench
模糊测试边界测试go test -fuzz
覆盖率质量评估go test -cover

Go 的测试工具链强大而简洁,善用它们能大幅提高代码质量。


分享这篇文章到:

上一篇文章
分布式任务调度
下一篇文章
Spring Cloud LoadBalancer