Go单元测试必须满足四条命名规则:文件名以_test.go结尾、函数名以Test开头且首字母大写、参数为*testing.T类型、无返回值;否则go test直接跳过。

Go单元测试必须满足的四个硬性命名规则
不满足这四条,go test 直接跳过函数,连报错都不会有。
- 测试文件名必须以
_test.go结尾(如mymath_test.go) - 测试函数名必须以
Test开头,且首字母大写(TestAdd✅,testAdd❌) - 函数参数必须是
*testing.T类型(func TestAdd(t *testing.T)) - 函数不能有返回值(
func TestAdd(t *testing.T) int会被忽略)
常见错误现象:运行 go test 显示 no tests to run,但文件和函数看着都“像”——八成是其中一条没守死。尤其注意大小写和下划线位置,Go 不会自动修正或提示具体哪条违规。
用 table-driven 测试替代 if 堆砌,避免重复逻辑
手写多个 if result != expected 不仅冗长,还容易漏掉边界 case;表格驱动把输入、预期、描述全收进 struct 切片里,一目了然,增删用例也不动主干逻辑。
推荐写法:
立即学习“go语言免费学习笔记(深入)”;
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
want int
}{
{"positive", 2, 3, 5},
{"zero", 0, 0, 0},
{"negative", -1, 1, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Add(tt.a, tt.b); got != tt.want {
t.Errorf("Add(%d,%d) = %d, want %d", tt.a, tt.b, got, tt.want)
}
})
}
}
关键点:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 加
t.Run后,每个子测试可单独执行(go test -run TestAdd/positive) - 子测试名带描述性字符串,失败时直接定位到哪个 case 出问题
- 结构体字段顺序建议按
name→ 输入 →want排列,保持可读性
基准测试函数必须用 Benchmark 前缀和 *testing.B 参数
和单元测试不同,基准测试不是验证“对不对”,而是测“快不快”。函数签名写错,go test -bench=. 就找不到它。
正确示例:
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(2, 3)
}
}
注意事项:
- 必须用
b.N控制循环次数,不能写死for i := 0; i ,否则结果不可比 - 如果函数内有初始化开销(比如打开文件、构建 map),用
b.ResetTimer()在循环前剔除这部分耗时 - 想测并发性能?加
b.RunParallel,但要注意被测函数是否线程安全 -
go test -bench=. -benchmem才能同时看到内存分配次数,光看 ns/op 容易误判
覆盖率统计和 CI 中容易被忽略的陷阱
go test -cover 报出 95% 覆盖率不等于代码质量高——它只统计“是否执行过”,不保证逻辑分支都被触发。
典型盲区:
- panic 分支、error 返回路径、
defaultcase 很容易漏测 - HTTP handler 测试中没 mock 依赖服务,实际跑的是集成行为,不是单元测试
- CI 中用
go test -coverprofile=coverage.out生成文件后,忘了用go tool cover -html=coverage.out查看具体哪行没覆盖 - 覆盖率阈值设太高(比如强制 100%),反而诱导开发者写无意义的“覆盖注水”测试
真正要盯住的,是那些你明确知道“这里可能出错”的地方有没有被测到,而不是数字本身。

















