如何在Golang中编写子测试Subtests Go语言t.Run结构化测试用例

2次阅读

t.run子测试必须在父测试返回前直接调用,否则静默跳过;斜杠用于分组但禁用首尾及连续双斜杠;setup需在t.run内或用t.cleanup复用;-run需带完整路径如testparse/valid_json

如何在Golang中编写子测试Subtests Go语言t.Run结构化测试用例

为什么 t.Run 里的测试不执行或 panic 了

子测试必须在父测试函数返回前被调用,否则会被忽略。常见错误是把 t.Run 放在 goroutine 里,或者放在 defer 中 —— 这两种情况子测试根本不会运行,也不会报错,只是静默跳过。

  • ✅ 正确:直接在 TestXxx 函数体中调用 t.Run("name", func(t *testing.T) { ... })
  • ❌ 错误:go t.Run(...)defer t.Run(...)
  • ⚠️ 注意:子测试内部若调用 t.Fatal / t.Skip,只影响当前子测试,父测试和其他子测试继续运行

t.Run 名字里带斜杠会怎样

Go 测试框架把 / 当作嵌套层级分隔符,比如 "JSON/valid""JSON/invalid" 会显示为树状结构。但名字不能以 / 开头、结尾,也不能连续出现 //,否则 go test 会报错:test name "xxx" contains illegal characters

  • ✅ 推荐命名:用下划线或短横线,如 "empty_slice""with_timeout"
  • ✅ 可用斜杠组织逻辑分组,如 "Parse/empty""Parse/malformed"
  • ❌ 避免:"/start""end/""a//b"

子测试里怎么共享 setup/teardown 逻辑

子测试之间默认不共享状态,每个子测试拿到的是独立的 *testing.T 实例。想复用初始化或清理代码,得手动控制生命周期 —— 不能依赖父测试的变量自动传递,尤其要注意并发安全。

  • ✅ 在 t.Run 内部做 setup(最安全):data := prepareTestData(); t.Run("case1", func(t *testing.T) { ... })
  • ✅ 若需跨子测试复用资源(如临时文件、mock server),在父测试中创建,并用 t.Cleanup 统一释放
  • ⚠️ 避免在父测试中修改全局变量或包级状态,多个子测试并行运行时可能互相干扰
  • ? 并行子测试要显式调用 t.Parallel(),且只能在 setup 完成后、实际断言前调用

go test -run 怎么精准匹配子测试名

-run 参数支持正则,但匹配的是完整测试路径,格式为 Parent/Sub1/Sub2。如果父测试叫 TestParse,子测试叫 "valid_json",那完整名字是 TestParse/valid_json

立即学习go语言免费学习笔记(深入)”;

  • ✅ 运行单个子测试:go test -run "TestParse/valid_json"
  • ✅ 运行某组子测试:go test -run "TestParse/JSON.*"
  • ✅ 忽略大小写:go test -run "(?i)testparse/valid"
  • ⚠️ 注意:-run "valid" 可能意外匹配到其他测试,务必带上父测试名前缀

子测试真正的复杂点不在写法,而在于「状态隔离」和「执行时机」——尤其当 setup 成本高、或需要 mock 全局行为时,很容易误以为子测试是顺序执行或共享上下文。别信直觉,每个 t.Run 都是独立的时空。

text=ZqhQzanResources