最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Golang单元测试与压力测试:提升语言学习项目质量的必要工具
时间:2026-07-25 08:51:48 编辑:袖梨 来源:一聚教程网
Go的Benchmark函数必须由go test -bench启动,不可手动调用;因其依赖testing.B实例动态管理b.N、计时、内存统计等,手动创建*testing.B会导致b.N=0、计时失效、结果失真。
Go 项目上线前不跑 go test,等于没测;只跑 go test -bench=. 当压力测试,等于白压。
go test 不执行测试函数?先查这四个硬约束
常见现象是终端输出 no test files 或 undefined: XXX,根本不是逻辑错,而是被 Go 测试机制卡住了:
- 文件名必须是
xxx_test.go(不能是test_xxx.go或xxx.test.go) - 测试函数名必须以大写
Test开头,且第二个字母不能是小写(TestAdd✅,Testadd❌,Testint❌) - 函数签名必须是
func TestXxx(t *testing.T)(参数类型错成*testing.B或自定义结构体就静默跳过) - 测试文件和被测代码必须在同一个包下(
package user的user.go,测试必须写在同目录的user_test.go中,且也声明package user)
导出问题常被忽略:如果被测函数首字母小写(如 calc()),它在同包内可被测试调用;但若你误建了 user_test 包(外部测试),那它就不可见——除非你真需要打破循环依赖,否则别这么干。
表驱动测试写法翻车最多的地方
用 []struct{ name, input, want string } + t.Run 是标准姿势,但实际写出来常漏掉关键细节:
立即学习“go语言免费学习笔记(深入)”;
- 每个
name必须能直接读出场景,比如"returns_error_on_empty_name",而不是"case1"——失败时你能一眼定位问题域 - 输入和预期值必须显式写出,别在循环里算
expected := tc.a + tc.b,否则失败日志只报got 5, want ??? - 必须包含非法输入 case:空字符串、负 ID、
nil指针、超长字段——这些才是线上 panic 的主力 - 闭包陷阱:循环中直接用
tc调t.Run,所有子测试实际跑的都是最后一个tc的数据;得写成tc := tc显式复制变量
复杂返回值别无脑 ==:比如返回 map[string]interface{},优先比关键字段(err == nil、user.ID > 0),而非全量 reflect.DeepEqual ——后者会让错误信息淹没在几百行 diff 里。
Benchmark 函数不是压力测试,别拿它当并发接口压测
go test -bench=. 跑的是基准测试(benchmark),目标是单次函数调用的 CPU/内存开销,不是模拟真实并发请求。它用 *testing.B,会自动循环调用并统计均值,但完全不涉及网络、连接池、goroutine 生命周期管理。
- 真正压测 HTTP 接口,得用独立工具:比如
go-wrk、hey,或自己写带连接复用的 goroutine 循环 - 压测脚本里不复用
http.Client,每轮新建 client → 连接泄露 → 内存暴涨 → 系统 kill 进程 - 本地压测高并发(如
-c 1000)前,务必加runtime.GOMAXPROCS(4)控制调度,否则 goroutine 创建速度远超调度能力,结果测的不是业务,是调度器瓶颈 - 关注
goroutine count变化趋势:压测前后用debug.ReadGCStats或 pprof 查/debug/pprof/goroutine?debug=2,持续上涨就是泄露
基准测试里 b.ResetTimer() 和 b.StopTimer() 的位置很关键:耗时操作(如初始化 DB 连接)必须放在 b.StopTimer() 之后、b.ResetTimer() 之前,否则会把 setup 时间算进 benchmark 结果。
覆盖率数字高 ≠ 质量高,重点盯住错误路径
go test -cover 输出一个百分比,但这个数字本身没意义。真正要命的是那些没覆盖的分支:
- if-else 的 else 分支、switch 的 default、error != nil 的处理块——这些才是线上 crash 的温床
- panic 路径、defer 中 recover 的逻辑、context 超时退出分支,往往一行没覆盖,上线后就 timeout
- 用
go tool cover -html=cover.out打开报告,直接点红块看哪行没执行,而不是盯着 summary 数字自我安慰
最常被忽略的一点:表驱动测试里的每个 t.Run 子测试,都必须独立触发所有分支路径。一个 case 覆盖了 err == nil,不代表另一个 case 就覆盖了 err != nil ——它们是隔离执行的。