Go 测试体系总览
Go 在标准库 testing 包中提供了三种互补的测试能力:单元测试验证正确性、基准测试评估性能、模糊测试(Go 1.18+)用随机输入发现边缘 bug。
1. 单元测试(Unit Test)
约定:文件名以 _test.go 结尾,函数名 Test 开头,参数 *testing.T:
func TestAdd(t *testing.T) {
if add(1, 2) != 3 {
t.Error("1 + 2 should be 3")
}
}
工程上推荐表格驱动测试(Table-Driven Tests),一组用例一张表,覆盖边界一目了然:
func TestAdd(t *testing.T) {
cases := []struct {
name string
a, b int
want int
}{
{"positive", 1, 2, 3},
{"zero", 0, 0, 0},
{"negative", -1, -2, -3},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := add(c.a, c.b); got != c.want {
t.Errorf("add(%d,%d) = %d, want %d", c.a, c.b, got, c.want)
}
})
}
}
go test -v ./... # 全部测试,显示详情
go test -run TestAdd # 只跑某个测试
go test -cover # 输出覆盖率
2. 基准测试(Benchmark)
函数名 Benchmark 开头,参数 *testing.B,被测代码必须放在 b.N 循环里(框架自动调整 N 直到测量稳定):
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
add(1, 2)
}
}
// 并行基准
func BenchmarkParallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
add(1, 2)
}
})
}
go test -bench . -benchmem # 时间 + 内存分配
go test -bench . -benchtime=2s # 指定单次时长
go test -bench . -cpu=1,4 # 对比不同核数
看结果时重点盯两列:ns/op(单次耗时)和 B/op(每次分配字节数)、allocs/op(分配次数)——内存分配往往是性能瓶颈。
3. 模糊测试(Fuzz)
函数名 Fuzz 开头,参数 *testing.F,先用 f.Add 提供种子语料,再写模糊目标:
func FuzzReverse(f *testing.F) {
f.Add("hello") // 种子
f.Add([]byte{0xff, 0x00})
f.Fuzz(func(t *testing.T, s []byte) {
got := reverse(s)
if !reflect.DeepEqual(reverse(got), s) {
t.Fatalf("reverse(reverse(%v)) != %v", s, s)
}
})
}
go test -fuzz FuzzReverse -fuzztime=10s
模糊测试自动生成输入、尝试让代码崩溃或违反断言,并在崩溃时自动最小化出最小复现用例。特别适合解析器、编解码、格式处理这类”输入空间大”的函数。
对比总结
| 类型 | 目的 | 前缀 | 参数 | 关键命令 |
|---|---|---|---|---|
| 单元测试 | 验证逻辑 | Test | *testing.T | -run / -cover |
| 基准测试 | 评估性能 | Benchmark | *testing.B | -bench / -benchmem |
| 模糊测试 | 发现边界 bug | Fuzz | *testing.F | -fuzz / -fuzztime |
最佳实践
- 单元测试覆盖分支与边界(空输入、nil、极值、超时);
- 基准测试关注 ns/op 与内存分配,优化高频调用路径;
- 模糊测试瞄准”输入复杂”的函数(解析器、编解码器);
- CI 里跑
go test -race ./...+-cover,把回归挡在上线前。


