go test 能运行不代表测试正确,必须严格遵循文件名(_test.go且同目录同包)、函数签名(func testxxx(t *testing.t))、断言方式(t.error/fatal)和表驱动写法(t.run分组),并使用-go test -v -count=1确保可定位、可验证。

go test 能直接跑起来,不代表测试写对了——很多失败不是逻辑错,而是文件名、函数签名、断言方式没踩中 Go 的硬性约定。
测试文件名和函数签名必须严格匹配
Go 不会自动识别任意 _test.go 文件:它只加载与被测代码同目录、且包名一致的测试文件。常见错误包括:
- 把
utils_test.go放错目录(比如放在test/子目录下)→go test找不到,静默跳过 - 测试文件用了
package main,但被测代码在package utils→ 编译报错cannot use utils.XXX (type int) as type int in argument to utils.XXX - 函数名写成
testAdd或Testadd→ 完全不执行,go test显示no tests to run - 参数类型写成
t testing.T(漏了指针)→ 编译失败:cannot use t (type testing.T) as type *testing.T
正确写法只有这一种:func TestAdd(t *testing.T),且必须和 Add 在同一包、同一目录。
用 t.Errorf 而不是 panic 或 log.Fatal
新手常误以为“报错就要立刻停”,于是写 if result != 5 { panic("fail") }。这会导致:
- 测试框架无法捕获错误,
go test认为测试崩溃而非失败,输出FAIL但无具体行号 - 后续测试用例不再执行,掩盖其他问题
- CI/CD 流水线里难以定位是 panic 还是逻辑错误
t.Error 和 t.Errorf 是唯一推荐的失败报告方式。它们标记当前测试为失败,但继续执行剩余断言(适合检查多个条件)。需要中断时用 t.Fatal/t.Fatalf,例如初始化失败时。
表驱动测试要加 t.Run 才能分组定位
只用 for range 遍历测试用例,失败时只能看到 TestAdd failed,不知道是哪组数据出问题。必须配合 t.Run:
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positive", 2, 3, 5},
{"zero", 0, 0, 0},
{"negative", -1, -1, -2},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("got %d, want %d", result, tt.expected)
}
})
}
}
这样失败输出会是 TestAdd/positive 或 TestAdd/negative,一眼锁定问题用例。不加 t.Run 就只剩模糊的 TestAdd。
go test 命令参数决定你看到什么
默认 go test 输出极简,容易误判结果:
-
go test:只显示ok ./math 0.001s或FAIL,不告诉你哪个函数挂了 -
go test -v:显示每个测试函数名和状态,失败时打印t.Error内容,必开 -
go test -run=TestAdd:只跑这个函数,调试时避免干扰 -
go test -count=1:禁用缓存,强制重新运行(防止因缓存误以为测试通过)
尤其注意 -count=1:Go 默认缓存成功测试结果,改了代码但没清缓存,go test 可能仍显示 (cached),让你以为改对了——其实根本没重跑。
TestXxx,而是让每个失败都可定位、每次修改都可验证。文件名、函数签名、t.Run、-count=1 这四点,漏一个就可能浪费半小时查不出为什么测试“看起来通过了”。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











