Go单元测试必须满足四个硬性条件:文件名以_test.go结尾、函数名以Test开头、参数为*testing.T、与被测代码同包;任一不满足则go test静默跳过。

Go 单元测试不是“配个框架就能跑”,而是靠 go test 命令和 testing 包原生驱动,但必须严格满足四个硬性条件:文件名以 _test.go 结尾、函数名以 Test 开头、参数为 *testing.T、与被测代码同包。漏掉任一条件,go test 会静默跳过——不报错、不提示、也不执行。
为什么 go test 没反应,或者提示 no test files?
最常见原因是文件或函数名没对上扫描规则。Go 的测试发现是纯字符串匹配,不是语义分析。
-
calc_test.go✅;test_calc.go❌(顺序反了);calc_tests.go❌(少下划线) -
func TestAdd(t *testing.T)✅;func testAdd(t *testing.T)❌(首字母小写);func Test_add(t *testing.T)❌(含下划线);func TestAdd(t testing.T)❌(漏掉*) - 测试函数写在
main.go里?go test直接忽略,哪怕它看起来像测试 - 被测函数是未导出的(小写开头),但测试文件放在另一个包(比如
calc_test)?无法访问,编译失败
怎么写一个不会被跳过的 Test 函数?
从最简结构开始,确保能被识别、能跑通、能报错:
- 新建
math_test.go,和math.go放同一目录、同 package - 函数签名必须是
func TestXxx(t *testing.T),不能多参、不能少星号、不能改名 - 断言必须用
t.Errorf或t.Fatal,fmt.Println或log.Print不会被go test当作失败 - 示例(验证加法):
func TestAdd(t *testing.T) {
got := Add(2, 3)
want := 5
if got != want {
t.Errorf("Add(2, 3) = %d, want %d", got, want)
}
}
运行 go test 输出 ok,加 -v 才能看到 === RUN TestAdd —— 全绿无输出是正常行为,不是卡住。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
立即学习“go语言免费学习笔记(深入)”;
多个用例怎么组织才不踩闭包坑?
用 t.Run 是必须项,不只是为了分组好看,而是解决循环变量捕获这个高频崩溃点。
- 错误写法:
for _, tc := range tests { t.Run(..., func(t *testing.T) { use(tc) }) }→ 所有子测试实际都用最后一个tc - 正确写法:循环内加一句
tc := tc,显式复制变量,再传进t.Run - 每个子测试必须带
name字段,否则失败时只显示TestParseURL/(空名),定位困难 - 检查 error 要写全逻辑:
if (err != nil) != tc.wantErr,而不是只比err.Error(),避免空指针或字符串误判
依赖 HTTP 或数据库时怎么测?
别急着引入 gomock 或 testify/mock。Go 的惯用法是把依赖抽成可替换字段或函数类型。
- 被测函数如果调用了
http.Get,不要 mockhttp.Get,而是改成接收一个doer func(string) (*http.Response, error)参数 - 测试时传入闭包:
func(url string) (*http.Response, error) { return &http.Response{StatusCode: 200}, nil } - 如果是结构体字段(如
Client *http.Client),测试时直接替换为带自定义Transport的实例,无需第三方库 - mock 库带来的行为偏差和维护成本,远高于用几行闭包或字段注入实现的隔离
真正容易被忽略的不是语法细节,而是测试文件位置和包名的一致性——它们决定了你能不能访问私有函数,也决定了 go test 能不能找到你的测试。写完先 go test -v 看是否列出了函数名,再谈逻辑。

















