Go 表驱动测试实战:用 t.Run 子测试组织可维护的单元测试

发布时间:2026/8/1 20:24:22

Go 表驱动测试实战:用 t.Run 子测试组织可维护的单元测试
Go 表驱动测试实战:用 t.Run 子测试组织可维护的单元测试你给一个函数写单测,第一版是复制粘贴:测输入 A 断言一段,测输入 B 再抄一遍断言,测边界值又抄一遍。三个用例还好,等长到十几个,某天函数签名改了,你得挨个改十几段几乎一样的代码。更糟的是,一个断言t.Fatal挂掉,后面的用例根本没跑,你不知道到底错了几个。Go 社区的标准答案是表驱动测试:把用例做成一张表,循环跑,配合t.Run拆成独立子测试。这篇手把手带你从「复制粘贴」重构到「一张表搞定」。被测函数拿一个简单的场景:把秒数格式化成人类可读的时长。packagetimeutilimportfmt// FormatDuration 把秒数格式化成 1h2m3s 这样的字符串funcFormatDuration(secint)(string,error){ifsec0{return,fmt.Errorf(秒数不能为负: %d,sec)}h,m,s:sec/3600,(sec%3600)/60,sec%60returnfmt.Sprintf(%dh%dm%ds,h,m,s),nil}朴素写法:复制粘贴,又臭又长第一版新手写法,每个用例抄一遍:funcTestFormatDuration_Naive(t*testing.T){got,_:FormatDuration(0)ifgot!0h0m0s{t.Fatalf(0 秒: 期望 0h0m0s, 得到 %s,got)}got,_FormatDuration(3661)ifgot!1h1m1s{t.Fatalf(3661 秒: 期望 1h1m1s, 得到 %s,got)}_,err:FormatDuration(-1)iferrnil{t.Fatal(负数应该报错)}}三个问题:断言逻辑重复三遍;t.Fatalf一旦触发,后面的用例直接不跑了;加用例只能继续往下抄。表驱动重构:用例是数据,断言只写一次核心思路是把「输入 期望输出」抽成一个结构体切片(就是那张「表」),断言逻辑写一遍,循环套用:funcTestFormatDuration(t*testing.T){// 这张表就是全部用例,加用例 加一行tests:[]struct{namestring// 用例名,出错时能一眼看出是哪个inputintwantstringwantErrbool}{{name:零秒,input:0,want:0h0m0s,wantErr:false},{name:一分钟,input:60,want:0h1m0s,wantErr:false},{name:时分秒齐全,input:3661,want:1h1m1s,wantErr:false},{name:负数报错,input:-1,want:,wantErr:true},}for_,tt:rangetests{got,err:FormatDuration(tt.input)if(err!nil)!tt.wantErr{t.Errorf(%s: 是否报错 %v, 期望 wantErr %v,tt.name,err!nil,tt.wantErr)continue}ifgot!tt.want{t.Errorf(%s: 得到 %q, 期望 %q,tt.name,got,tt.want)}}}注意这里用t.Errorf而不是t.Fatalf——Errorf标记失败但继续跑,所以一次能看到所有失败的用例,而不是碰到第一个就停。加新用例只需在表里加一行,断言逻辑一个字都不用动。升级:t.Run 拆成独立子测试上面的版本还有个小缺点:所有用例挤在一个TestFormatDuration里,测试输出看不清哪个子用例过了、哪个挂了,也没法单独跑某一个。用t.Run给每个用例开一个子测试就解决了:funcTestFormatDuration(t*testing.T){tests:[]struct{namestringinputintwantstringwantErrbool}{{name:零秒,input:0,want:0h0m0s},{name:一分钟,input:60,want:0h1m0s},{name:时分秒齐全,input:3661,want:1h1m1s},{name:负数报错,input:-1,wantErr:true},}for_,tt:rangetests{// 每个用例一个子测试,名字就是 tt.namet.Run(tt.name,func(t*testing.T){got,err:FormatDuration(tt.input)if(err!nil)!tt.wantErr{t.Fatalf(是否报错 %v, 期望 %v,err!nil,tt.wantErr)}ifgot!tt.want{t.Errorf(得到 %q, 期望 %q,got,tt.want)}})}}t.Run的好处:输出里每个子测试独立显示,--- FAIL: TestFormatDuration/负数报错一眼定位。可以只跑某一个:go test -run TestFormatDuration/时分秒齐全。子测试之间隔离,某个子测试t.Fatalf只终止它自己,不影响其余子测试继续跑。跑一下看输出:$ gotest-v-runTestFormatDurationRUN TestFormatDurationRUN TestFormatDuration/零秒RUN TestFormatDuration/一分钟RUN TestFormatDuration/时分秒齐全RUN TestFormatDuration/负数报错 --- PASS: TestFormatDuration(0.00s)--- PASS: TestFormatDuration/零秒(0.00s)--- PASS: TestFormatDuration/一分钟(0.00s)--- PASS: TestFormatDuration/时分秒齐全(0.00s)--- PASS: TestFormatDuration/负数报错(0.00s)PASS一个必须知道的坑:循环变量与 t.Parallel如果你想让子测试并行跑(t.Parallel()),在Go 1.22 之前有个经典陷阱:所有子测试闭包共享同一个循环变量tt,并行执行时它们读到的都是循环结束后的最后一个值。老代码里你会看到这种「重新赋值」的写法来规避:for_,tt:rangetests{tt:tt// Go 1.22 前必须这行,给每次迭代一份独立副本t.Run(tt.name,func(t*testing.T){t.Parallel()// 标记为并行,和其他并行子测试一起跑got,_:FormatDuration(tt.input)ifgot!tt.want{t.Errorf(得到 %q, 期望 %q,got,tt.want)}})}Go 1.22 起循环变量每次迭代都是新的,tt : tt这行可以删掉了。但如果你的项目还跑在老版本,或者你不确定别人的环境,留着它最保险——它无害。不加t.Parallel()时子测试是串行的,不受这个坑影响,但加了并行又忘了处理循环变量,就会得到「所有用例都在测最后一行数据」的诡异结果。小结别复制粘贴断言:把用例抽成结构体切片(表),断言逻辑只写一遍,循环套用。表里给每个用例一个name字段,出错能一眼定位是哪个用例。用t.Run(tt.name, ...)拆成子测试:输出清晰、可单独跑、子测试之间隔离。收集所有失败用t.Errorf(继续跑),需要立即终止当前子测试才用t.Fatalf。并行子测试注意循环变量陷阱:Go 1.22 前要写tt : tt,1.22 起可省;不确定就留着。一句话记忆:用例是数据、断言是代码,一张表 t.Run就是 Go 测试的标准姿势。

相关新闻

仓颉开源社区学习赛火热开赛!提交 PR 领京东卡,等你来抢!

仓颉开源社区学习赛火热开赛!提交 PR 领京东卡,等你来抢!

2026/8/1 20:24:22

你有没有想过——自己的代码被成千上万的开发者使用,会是什么感觉?又或者,你一直想参与开源,却总觉得门槛太高、不知从何下手? 「仓颉开源社区学习赛」正是为这样的你准备的。 简单来说就是:在仓颉开源社区…

仅限前500名技术管理者开放:AI信息归类成熟度评估矩阵(含5级分级标准+自检SOP+差距修复路线图)

仅限前500名技术管理者开放:AI信息归类成熟度评估矩阵(含5级分级标准+自检SOP+差距修复路线图)

2026/8/1 20:14:22

更多请点击: https://intelliparadigm.com 第一章:AI信息归类整理的底层逻辑与战略价值 AI信息归类整理并非简单的标签堆叠或关键词匹配,其底层逻辑根植于语义理解、上下文建模与知识图谱驱动的结构化推理。现代大语言模型通过嵌入空间对非结…

【AI数据质量检查黄金法则】:20年专家亲授5大致命陷阱与实时修复框架

【AI数据质量检查黄金法则】:20年专家亲授5大致命陷阱与实时修复框架

2026/8/1 20:14:22

更多请点击: https://kaifayun.com 第一章:AI数据质量检查黄金法则的底层逻辑 AI模型的性能上限,本质上由训练数据的质量决定——而非算法复杂度或算力规模。高质量数据并非“越多越好”,而是要求在完整性、一致性、准确性、时效…

【通义千问v2.3表格识别新特性】:支持复杂嵌套表+手写体混合识别,仅限首批白名单用户接入

【通义千问v2.3表格识别新特性】:支持复杂嵌套表+手写体混合识别,仅限首批白名单用户接入

2026/8/1 21:14:24

更多请点击: https://intelliparadigm.com 第一章:通义千问表格识别能力全景概览 通义千问(Qwen)在多模态理解任务中展现出强大的表格识别(Table Recognition)能力,支持从扫描文档、截图、PDF …

深入剖析 Vue 自定义指令:核心原理与高频应用场景

深入剖析 Vue 自定义指令:核心原理与高频应用场景

2026/8/1 21:14:24

深入剖析 Vue 自定义指令:核心原理与高频应用场景1. 前言 在 Vue.js 的官方文档中,我们经常看到 v-model、v-if、v-for 等内置指令。它们帮助我们实现了数据与视图的绑定、条件渲染和列表渲染。然而,有时为了复用通用的 DOM 操作逻辑&#xf…

戴森球计划工厂蓝图选择终极指南:新手到高手的完整布局秘籍

戴森球计划工厂蓝图选择终极指南:新手到高手的完整布局秘籍

2026/8/1 21:14:24

戴森球计划工厂蓝图选择终极指南:新手到高手的完整布局秘籍 【免费下载链接】FactoryBluePrints 游戏戴森球计划的**工厂**蓝图仓库 项目地址: https://gitcode.com/GitHub_Trending/fa/FactoryBluePrints FactoryBluePrints是戴森球计划最全面的蓝图仓库&am…

10分钟终极指南:彻底解决TranslucentTB启动时VCLibs依赖缺失问题

10分钟终极指南:彻底解决TranslucentTB启动时VCLibs依赖缺失问题

2026/8/1 21:14:24

10分钟终极指南:彻底解决TranslucentTB启动时VCLibs依赖缺失问题 【免费下载链接】TranslucentTB A lightweight utility that makes the Windows taskbar translucent/transparent. 项目地址: https://gitcode.com/gh_mirrors/tr/TranslucentTB 你是否下载了…

SAP MM 查看谁操作的单据-找出罪魁祸首!

SAP MM 查看谁操作的单据-找出罪魁祸首!

2026/8/1 21:14:24

SM20 找出操作单据或者过账单据的人其他常用: SCU3:可以看当前谁在操作哪些事务代码,并且还可以剔除类似SM12 SM12:踢人看锁表 SM13:系统函数有时候更新失败会在这里可以看得到 等等吧,后面再补充 以上只是个人理解分享的笔记仅供参考&#…

vg-renderer:为bgfx量身打造的高性能矢量图形渲染解决方案

vg-renderer:为bgfx量身打造的高性能矢量图形渲染解决方案

2026/8/1 21:04:24

vg-renderer:为bgfx量身打造的高性能矢量图形渲染解决方案 【免费下载链接】vg-renderer A vector graphics renderer for bgfx, based on ideas from NanoVG and ImDrawList (Dear ImGUI) 项目地址: https://gitcode.com/gh_mirrors/vg/vg-renderer 在游戏开…

[具身智能-649]:个人电脑搭建 RTSP 服务完整方案(Windows / Ubuntu 双平台,适配 RDK X5 rtsp2display 调试)

[具身智能-649]:个人电脑搭建 RTSP 服务完整方案(Windows / Ubuntu 双平台,适配 RDK X5 rtsp2display 调试)

2026/8/1 16:37:34

目标:电脑作为RTSP 服务端,循环推送 H264/H265 视频流; RDK X5 通过 rtsp2display 拉流预览,完全不需要在开发板编译 live555。 提供两套成熟方案: ✅ 方案 A:FFmpeg(最简单,优先推…

PDF合并与动态水印的工程化方案:2026国内免费工具实测对比

PDF合并与动态水印的工程化方案:2026国内免费工具实测对比

2026/8/1 0:15:49

一、背景与测试方案 在实际项目交付中,PDF文件合并与版权保护水印的叠加是一个高频但容易被低估的技术需求。典型的处理链路涉及:多源PDF的文件流合并、页面级水印渲染(含透明度混合与图层叠加)、输出文件体积控制。看似简单的操作…

PDF拆分压完图糊了?2026国内免费实测,档案员都在用的组合方案

PDF拆分压完图糊了?2026国内免费实测,档案员都在用的组合方案

2026/8/1 4:47:48

说实话,提到PDF拆分再压缩,我真是被折腾得够呛。 上个月公司年度合同归档,一份300多页的PDF总合同,需要按年份拆分成三个独立文件,再分别压缩到10MB以内方便邮件发送各部门确认。我心想这还不简单?先找个海…

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

2026/8/1 0:03:03

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…

导师推荐!2026最新AI论文工具测评与实用推荐

导师推荐!2026最新AI论文工具测评与实用推荐

2026/8/1 0:03:03

2026年真正好用的AI论文工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

告别游戏崩溃:XCOM 2模组管理器的智能革命

告别游戏崩溃:XCOM 2模组管理器的智能革命

2026/8/1 0:03:03

告别游戏崩溃:XCOM 2模组管理器的智能革命 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_mirrors/xc/xcom2-lau…

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

2026/8/1 0:03:03

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…

导师推荐!2026最新AI论文工具测评与实用推荐

导师推荐!2026最新AI论文工具测评与实用推荐

2026/8/1 0:03:03

2026年真正好用的AI论文工具,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

告别游戏崩溃:XCOM 2模组管理器的智能革命

告别游戏崩溃:XCOM 2模组管理器的智能革命

2026/8/1 0:03:03

告别游戏崩溃:XCOM 2模组管理器的智能革命 【免费下载链接】xcom2-launcher The Alternative Mod Launcher (AML) is a replacement for the default game launchers from XCOM 2 and XCOM Chimera Squad. 项目地址: https://gitcode.com/gh_mirrors/xc/xcom2-lau…