6.3 测试:表格驱动、基准与覆盖率


6.3 测试:表格驱动、基准与覆盖率

本节摘要:Go 把测试做成语言级公民:测试文件与源码同目录,测试、基准、示例三类函数各有命名规则,一个 go test 命令统一驱动。本节覆盖测试文件结构、表格驱动加子测试的组织方式、基准测试的统计读法、示例函数的文档兼测试双重身份、mock 与桩的策略、集成测试的构建标签隔离、覆盖率统计,以及并发代码的测试纪律。

本节目标

阅读完本节,你应当能够:

  1. 写出符合命名规则的测试、基准、示例函数;
  2. 用表格驱动加子测试组织多场景用例;
  3. 读懂基准输出的每列指标并避免常见误读;
  4. 用接口加轻量桩替身外部依赖;
  5. 用构建标签隔离集成测试、统计覆盖率;
  6. 安全地测试并发代码。

一、命名即协议

测试文件名以下划线 test 结尾、与被测文件同目录;三类函数按名字识别:

函数形态 用途 签名
TestXxx 功能测试 接收测试状态指针
BenchmarkXxx 性能基准 接收基准状态指针
ExampleXxx 可执行文档 无参数,输出注释校验
func TestDivide(t *testing.T) { got, err := divide(10, 2) if err != nil || got != 5 { t.Errorf("得到 %d, %v,想要 5, nil", got, err) } }

测试失败不 panic 而是调 Error 或 Fatal(后者中止当前测试),报告自带文件行号。go test 跑当前包,go test 加点号跑全部,加详细标志显示每个用例名。

二、表格驱动:Go 的测试美学

多个输入输出场景堆 if 是初学者写法;Go 的惯用法是把场景做成数据表,循环执行:

func TestParse(t *testing.T) { tests := []struct { name string input string want int wantErr bool }{ {"正常整数", "42", 42, false}, {"负数", "-7", -7, false}, {"非数字", "abc", 0, true}, {"空串", "", 0, true}, } for _, tt := range tests { tt := tt // 固定迭代变量(老版本必需) t.Run(tt.name, func(t *testing.T) { // 子测试 got, err := parse(tt.input) if (err != nil) != tt.wantErr { t.Fatalf("错误=%v, 期望出错=%v", err, tt.wantErr) } if got != tt.want { t.Errorf("得到 %d, 想要 %d", got, tt.want) } }) } }

两个好处:新增场景只加一行数据;子测试可单独重跑(命令里加斜杠用例名)、失败报告按场景分组。name 字段写人话,失败信息就自带诊断上下文。

三、基准测试:数字要会读

func BenchmarkFib(b *testing.B) { for i := 0; i < b.N; i++ { fib(20) } }

运行时框架自动调整迭代次数到稳定耗时,输出形如"每操作 45 纳秒、每次分配 2 个对象、共 48 字节"。读数纪律:比较要有对照(改动前后同机同负载)、注意编译器死码消除(把结果赋给包级 sink 变量防被优化掉)、分配数常比时间更稳定,优化内存分配往往比抠 CPU 更见效。加并发标志可测多 goroutine 下的竞争吞吐。

四、示例函数:能跑的文档

func ExampleHello() { fmt.Println(hello("Go")) // Output: hello, Go }

Output 注释是断言:输出不符测试失败。它同时被文档工具抓取展示在函数文档旁——文档错了测试就红,一鱼两吃。

五、mock 与桩:接口是替身工厂

第 3 章"接口放消费者侧"在这里兑现价值。被测的 service 依赖 repo 接口,测试里塞一个内存实现的假 repo:

type fakeRepo struct{ users map[int]User } func (f *fakeRepo) Get(id int) (User, error) { u, ok := f.users[id] if !ok { return User{}, errors.New("未找到") } return u, nil }

手写假实现几十行、零依赖、类型安全,中小项目足够;只有当接口庞大或行为复杂(模拟超时、错误注入矩阵)时才引第三方 mock 框架。桩与假的区别记一句:桩返回死数据,假有真行为(内存版真实现)。

六、集成测试与覆盖率

单元测试不碰网络数据库;集成测试要碰,用构建标签隔离——文件头一行注释式标签,默认构建不带它,命令加标签才编译执行。CI 里两阶段跑:快速单元挡门,集成夜里全量。

覆盖率统计一个标志搞定,输出"语句覆盖百分比",并可生成 HTML 报告逐行标色。正确心态:覆盖率是找漏测的地图,不是考核指标——百分比崇拜会催生无断言的垃圾测试;一条会失败的断言,胜过十行只为覆盖分支的调用。

图:测试金字塔与工具分布

图:测试金字塔与工具分布

七、并发代码的测试纪律

并发 bug 藏在交错序列里,测试要做到三件事:开竞态检测跑测试(运行时抓数据竞争现形);用 WaitGroup 或 channel 等待完成,绝不用睡眠赌时序压测式循环(同一并发测试跑几百次)让低概率交错显形。测试 HTTP 处理函数时,标准库自带测试服务器与假请求构造,不必起真端口。

⚠️ 常见坑:基准测试里被测结果没人用,编译器整段优化掉,测了个寂寞——结果赋给包级变量。
💡 关键直觉:测试是使用文档的第一稿——写测试难受,说明被测接口设计得难用,先改接口再补测试。

八、测试命令速查

问答补遗。测试该写多少? 覆盖核心分支与所有已修过的 bug——每个线上 bug 补一个回归用例,是最划算的投入。私有函数怎么测? 同包测试天然可见,直接测即可;若私有逻辑复杂到需要独立测试,通常说明它想被抽成独立类型。测试里能打日志吗? 能,日志函数配详细标志才输出,平时保持安静;断言失败信息要写成"得到什么、想要什么"的对照句式。

核心回顾

  • 命名即协议:Test、Benchmark、Example 三类函数加 go test 单命令驱动。
  • 表格驱动加子测试是 Go 的组织美学,加场景只加一行。
  • 基准读数:对照实验、防死码消除、关注分配数。
  • 示例函数:Output 注释即断言,文档与测试合一。
  • 替身策略:接口加手写假实现优先,重型 mock 框架按需。
  • 集成测试用构建标签隔离,覆盖率当地图不当 KPI。
  • 并发测试三件套:竞态检测、显式等待、压测循环。

下一节盘点弹药库:常用标准库与扩展库。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U