go测试函数名必须以test开头且首字母大写,签名须为func(t *testing.t),文件名以_test.go结尾;推荐表格驱动测试,用t.run隔离用例,避免panic而用t.errorf/t.fatal,测试文件仅导入必要依赖。

test函数名必须以Test开头且首字母大写
Go的testing包靠函数名识别测试用例,不是靠注解或配置。如果写成func testAdd()或func Testadd(),go test直接无视它——既不报错也不运行。
实操建议:
-
Test后面必须紧跟大写字母,比如TestAdd合法,Test_add不合法 - 函数签名必须是
func(t *testing.T),少个*testing.T参数也会被跳过 - 文件名得是
_test.go结尾,比如calc_test.go,否则go test连文件都不读
用t.Errorf比panic更安全
在测试里用panic会中断整个测试流程,导致后续用例全被跳过;而t.Errorf只标记当前用例失败,其他测试照常执行。
常见错误现象:改一个bug时顺手加了panic("TODO"),结果跑go test只看到第一个失败就停了,误以为只剩一个问题。
立即学习“go语言免费学习笔记(深入)”;
实操建议:
- 所有断言失败都走
t.Errorf,别图快写if !ok { panic(...) } - 想提前退出当前测试用例?用
t.Fatal,它比panic语义清晰,且不会影响其他测试函数 - 注意
t.Fatal和t.Fatalf会终止当前函数,但不会终止整个go test进程
表格驱动测试(table-driven tests)不是可选项
手写一堆TestAdd1、TestAdd2,维护成本高、易漏边界。Go社区默认实践就是用切片定义测试用例,for循环跑完——这不是风格偏好,是避免重复逻辑和遗漏场景的实际需要。
性能影响很小,但可读性和覆盖能力差一个数量级。
实操建议:
- 每个测试函数里定义
tests := []struct{ input, want int }{...},结构体字段名尽量反映业务含义(比如name、errWant) - 循环里用
t.Run包裹单条用例,名字带上输入值:t.Run(fmt.Sprintf("input=%d", tt.input), func(t *testing.T) {...}),失败时能立刻定位到哪组数据出问题 - 别省略
t.Run——没有它,所有用例共用同一个t上下文,t.Fatal会误杀其他用例
测试文件里别import非测试依赖
测试文件(*_test.go)被go test单独编译,但它仍参与模块依赖分析。如果在xxx_test.go里import了生产代码没用到的第三方库(比如github.com/sirupsen/logrus),会导致go build正常,但go test失败:找不到该包,或版本冲突。
更隐蔽的问题是:某些工具链(如Bazel、CI中的最小依赖构建)会严格区分测试/生产依赖,多引一个包就卡住。
实操建议:
- 测试文件只import标准库 + 当前包 +
testify这类纯测试辅助库(且确认项目已声明为dev dependency) - 想打日志?用
t.Log,别引入logrus/zap - 不确定某包是否必要?删掉它,跑一遍
go test -v,看是否真报错
边界情况永远比文档写的多,比如t.Parallel()在子测试里调用要小心竞态,或者os.Setenv没清理干净污染其他测试——这些得靠实际跑起来才暴露。










