go测试文件必须以_test.go结尾且测试函数需为testxxx(t *testing.t)形式,与被测代码同包;循环测试须用t.run和tt := tt避免变量捕获;表驱动测试应命名清晰、明确校验error和复杂返回值;i/o测试需内存模拟或临时文件隔离;时间依赖须通过参数注入而非全局patch。

Go 的测试文件不是“能跑就行”,而是必须满足几个硬性条件,否则 go test 根本不会识别、也不会执行——这不是报错,是静默跳过。
文件名和函数签名必须严格匹配约定
Go 测试系统只认两种东西:_test.go 结尾的文件,以及函数名以 Test 开头、参数为 *testing.T 的函数。大小写和下划线都敏感。
-
calc_test.go✅ 合规;test_calc.go❌ 不加载 -
func TestAdd(t *testing.T)✅;func testAdd(t *testing.T)或func Test_add(t *testing.T)❌ - 测试函数必须和被测代码在同一个包里,不能放在
internal/testutil这类独立包中——否则访问不到未导出字段或函数
t.Run 是避免变量捕获错误的必要手段
循环中写多个测试用例却不加 t.Run,会导致所有子测试共享最后一次迭代的变量值——这是新手最常踩的坑,失败时根本看不出是哪个 case 出了问题。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 必须在循环内第一行写
tt := tt显式复制结构体变量 - 子测试名别含空格或斜杠(如
"case/1"),否则go test -run TestParse/case/1会在 shell 层面解析失败 -
t.Parallel()只能放在t.Run内部,且子测试之间不能有共享状态(比如全局 map、临时文件句柄)
表驱动测试要聚焦可验证字段
表驱动不是为了“看起来高级”,而是为了快速加 case、一眼看出覆盖缺口、避免 if 嵌套爆炸。每个字段命名要直白,别缩写。
- 用
name、input、want、errIsNil,别用tc或data - 对
error的检查必须明确预期:正常路径要if err != nil报错,异常路径要if err == nil报错 - 复杂返回值别用
==比较,改用reflect.DeepEqual(got, tt.want),否则 struct 字段顺序或指针差异会误报
文件 I/O 测试必须隔离磁盘依赖
直接读写真实文件会让测试变慢、不稳定、残留垃圾。正确做法是抽象接口 + 内存模拟,或用系统临时目录并强制清理。
- 优先用
os.CreateTemp("", "test-*.txt")创建隔离临时文件,defer os.Remove(tmpFile.Name())清理 - 若函数接收
io.Reader,用bytes.NewBufferString("...")或io.NopCloser(...)注入内存流,绕过磁盘 - 别在测试里调
os.ReadFile读 fixture 文件——那已不是单元测试,是集成测试,且路径易错、不可靠
最易被忽略的是:测试文件里写了 time.Now = func() time.Time {...} 这类全局 patch,在并发子测试中会互相覆盖,而且无法回滚。真正可控的做法,是把时间依赖抽成函数类型参数传入。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










