subtest 名字必须唯一且避免斜杠,否则测试静默跳过;并行时禁用 t.cleanup;失败默认不中断父测试;命名推荐带上下文如 "with_empty_slice"。

subtest 名字不能重复,否则测试会静默跳过
Go 的 t.Run 在同一层级下遇到重名 subtest 时不会报错,而是直接跳过后续同名的测试——你写的代码执行了,但没跑,还看不出问题。
常见错误现象:改了测试逻辑,结果旧断言还在通过,新 case 没生效;或者 CI 上偶尔“漏测”几个分支。
- 名字建议带上下文,比如
"with_empty_slice"、"with_timeout_10ms",别用"valid"这种泛称 - 如果循环生成 subtest,务必把变量值嵌入名字,避免闭包捕获导致全部变成最后一个值:
t.Run(fmt.Sprintf("case_%d", i), func(t *testing.T) { ... }) - 运行时加
-v参数可看到实际执行了哪些 subtest,方便核对是否遗漏
t.Run 内部不能用 t.Parallel() 和 t.Cleanup 混用
虽然语法上允许,但 t.Parallel() 会让 subtest 脱离父 test 的生命周期管理,而 t.Cleanup() 是绑定在当前 *testing.T 实例上的。一旦 subtest 并行执行,Cleanup 函数可能在父 test 已结束时才被调用,引发 panic 或资源泄漏。
使用场景:需要 setup/teardown 的参数化测试(如临时文件、mock server)。
立即学习“go语言免费学习笔记(深入)”;
- 并行 subtest 中 cleanup 必须自己管理,比如用
defer os.Remove(...)或显式关闭 - 若坚持用
t.Cleanup,就别调t.Parallel();反之亦然 - 注意
t.Parallel()只对同级 subtest 生效,嵌套 subtest 调用它无效
子测试失败时默认不中断父测试,但调试时容易误判执行路径
Go 测试默认是“继续执行”,哪怕某个 t.Run 失败,其余 subtest 还是照跑。这本是优点,但新手常以为“第一个 fail 就停了”,结果看到最终失败数比预期少,怀疑逻辑没覆盖全。
性能影响:大量 subtest + 高频失败时,日志刷屏,定位成本上升。
- 加
-failfast参数让整个测试包在首个失败时退出(注意:不是单个 TestXxx 函数,而是整个go test进程) - 想只对某个 TestXxx 做 fail-fast,得手动检查
t.Failed()后调t.Skip(),但不推荐——破坏 subtest 独立性 - CI 环境建议保留默认行为,确保一次发现所有问题;本地调试可配合
-run="TestXxx/with_.*"精确过滤
测试名中的斜杠 / 会被 go test 当作嵌套标识,影响 -run 过滤
t.Run("http/client timeout") 这样的名字,会被解析成三层结构:TestXxx → http → client timeout。如果你用 -run="TestXxx/http",它真会匹配到这个 subtest,但你本意可能是模糊搜索 “http” 关键词。
兼容性影响:Go 1.21+ 对斜杠处理更严格,某些 IDE 插件或 CI 工具的测试导航可能异常。
- 避免在 subtest 名里用
/、\、.,用下划线或中划线替代,比如"http_client_timeout" - 如果必须表达层级(如 API 版本),用双冒号更安全:
"v1::create_user",它不会被 go test 解析为嵌套 - 运行前用
go test -list=.看实际解析出的测试树,验证命名是否符合预期
subtest 真正难的不是写法,是名字怎么起、失败时谁该停、cleanup 怎么不和 parallel 打架——这些地方没显式报错,但跑偏了很难回溯。










