go表驱动测试核心是用切片存结构体用例,字段含input、want、name等;必用t.run命名子测试并避免闭包变量捕获;reflect.deepequal仅适用于无函数/互斥锁的复合类型,浮点数需用math.abs比较。

Go 测试里怎么组织 table driven test 的结构
核心就一条:用切片存测试用例,每个元素是结构体,字段覆盖输入、期望输出、可选描述。别用 map 或独立变量拼凑,结构体自带字段语义,IDE 能跳转,出错时 t.Errorf 里也容易定位。
常见错误是把输入和期望拆成两个平行切片,比如 inputs []string 和 expects []int —— 一旦增删中间用例,极易错位,且无法加注释说明“这个 case 是测空字符串边界”。
- 结构体字段名保持简洁,如
input、want(不用expected)、name(可选,用于t.Run) - 所有字段尽量导出(首字母大写),否则 JSON 解析或反射工具读不到(虽然 table test 一般不用反射,但留余地)
- 如果某个 case 需要特殊 setup(比如临时文件、mock client),把它塞进结构体里,而不是在循环外写条件分支
为什么必须用 t.Run 包一层
不包就只能看到 “test failed”,不知道是第几个 case 崩了;包了就能精准定位到 TestParseURL/empty_string 这种带名字的子测试。更重要的是,并行执行(t.Parallel())必须依赖 t.Run,否则并发跑会互相干扰 t.Log 和失败信息。
典型坑:在 t.Run 闭包里直接引用循环变量,比如 for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { ... use tc.input ... }) } —— Go 中闭包捕获的是变量地址,所有子测试实际共用最后一个 tc 值。
- 正确写法:在循环体内声明新变量,
tc := tc - 或者直接用索引:
for i := range cases { tc := cases[i]; t.Run(tc.name, ...) } - 别省略
name字段,空字符串会让t.Run生成无意义名字,排查时多一重猜测成本
reflect.DeepEqual 在 table test 里怎么用才安全
它适合比对 struct、slice、map 等复合类型,但不是万能的。最常踩的坑是拿它比对含函数字段、含 sync.Mutex、含 unsafe.Pointer 的 struct —— 会 panic 或返回不可靠结果。
另一个隐形问题:浮点数比较。如果 want 是 float64,用 DeepEqual 会因精度丢失误判失败,比如 0.1+0.2 != 0.3。
- 比对浮点数改用
math.Abs(got - want) - 比对 error 优先用
errors.Is或errors.As,而不是DeepEqual(err, tc.wantErr)(error 实现可能含未导出字段) - 比对 time.Time 用
.Equal()方法,DeepEqual会受时区、单调时钟等影响 - 如果结构体字段太多,只关心其中几个,别硬套
DeepEqual,手动比关键字段更清晰
表格数据从哪来:硬编码、JSON 文件还是生成代码
小项目、逻辑稳定、case 不超过 10 个,直接硬编码在测试文件里最省事。别为了“可维护”提前引入外部文件,反而让新人看不懂测试在哪定义、case 怎么来的。
但当 case 数量上 20,或需要多人协作维护(比如协议解析测试集),JSON 文件就值得考虑。注意路径处理:用 filepath.Join("testdata", "parse_cases.json"),别写死相对路径;读取时加 if _, err := os.Stat(path); os.IsNotExist(err) { t.Skip("test data not found") },避免 CI 环境漏传文件导致测试静默跳过。
- 生成代码(如 go:generate)适合超大规模 case(如 Unicode 正则测试),但调试成本高,普通业务逻辑没必要
- 别把测试数据放
testdata/下还用go:embed——embed是为二进制打包设计的,测试时文件缺失反而难诊断 - JSON 里避免用注释,Go 的
json.Unmarshal不支持;想加说明,就加desc字段
table driven test 真正难的不是写法,而是判断哪些逻辑值得抽象成表、哪些 case 必须保留独立测试(比如涉及真实网络或磁盘 IO)。边界 case 往往藏在 if 分支深处,而不是表格第一行。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











