go测试有效需满足命名、文件位置、纯函数优先等严格条件:testxxx函数、_test.go文件、同包同目录;优先测无副作用逻辑函数,避免time.now等非确定操作,error用errors.is判断,main逻辑应拆出可测函数。

go test 能跑起来,不等于测试真正有效——很多初学者写的测试只是“能过”,但一改逻辑就漏覆盖、一加新分支就崩、一换环境就失败。
Test函数命名与文件位置必须严格匹配
Go 不接受商量:测试函数名必须是 TestXxx(首字母大写),参数必须是 *testing.T;测试文件必须以 _test.go 结尾,且和被测代码在同一个包、同一目录。
常见错误现象:
- 写成
testAdd或test_add→go test完全无视,零用例运行 - 把
math_test.go放到tests/子目录 → 测试无法导入被测的math包,报undefined: Add - 在 Go Playground 粘贴测试代码 → Playground 不支持
go test生命周期,os.Stdin重定向失效,t.Run会 panic
实操建议:
- 用
go test -v运行,看输出里有没有列出你写的TestXxx名字;没有,先查命名和文件后缀 - 本地终端进到含
.go和_test.go的目录再执行,别跨目录调用
纯逻辑函数优先测,别急着 mock 数据库
刚学 Go 时最容易走偏:一上来就想测 HTTP handler 或 main 入口,结果卡在 log.Fatal、os.Exit、stdin 交互上。其实最该先测的是没副作用的函数——比如 IsValidEmail、ParseDuration、CalculateTax。
实操建议:
- 把表驱动测试写成结构体切片:
tests := []struct{ input string; expected bool }{...},比一堆if判断清晰得多 - 避免在测试里调用
time.Now()或rand.Intn()—— 它们让测试非确定;需要时间时,把 now 作为参数传入 - 别 mock
json.Marshal或strings.TrimSpace,它们是纯函数,mock 只增加噪音、掩盖真实行为
error 判断不能只看 nil,得区分类型
很多测试只写 if err != nil 就完事,结果函数返回 os.ErrNotExist 时测试通过,但线上遇到磁盘不存在却 panic —— 因为没验证是否真该返回那个 error。
实操建议:
- 用
errors.Is(err, os.ErrNotExist)替代err.Error() == "no such file",前者支持包装链,后者脆弱且不国际化 - 如果函数可能返回多种 error(如
ErrNotFound/ErrPermissionDenied),每个都要有对应测试 case - 注意
(*User, error)这类双返回值:不能只断言user != nil,必须同时确认err == nil
main 函数本身不该被测,逻辑要抽出来
main 函数里混着 log.Fatal、os.Exit、配置加载、HTTP 启动……它天生不可测。强行测只会让你陷入重定向 stdout、捕获 exit、patch log 的泥潭。
实操建议:
- 把核心逻辑(如读配置、校验参数、计算结果)拆成独立函数,返回
error而不是调用log.Fatal -
main只做胶水层:调用逻辑函数 → 检查 error → 决定是否log.Fatal或os.Exit - 例如
readConfig("config.json")应返回(Config, error),而不是内部log.Fatal掉整个进程
真正的难点不在写 TestXxx,而在于让被测函数本身愿意被测——它得足够小、无副作用、依赖可注入。这点比任何测试技巧都关键。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











