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 的测试工具链强大而简洁,善用它们能大幅提高代码质量。