本节摘要:Go 把测试做成语言级公民:测试文件与源码同目录,测试、基准、示例三类函数各有命名规则,一个 go test 命令统一驱动。本节覆盖测试文件结构、表格驱动加子测试的组织方式、基准测试的统计读法、示例函数的文档兼测试双重身份、mock 与桩的策略、集成测试的构建标签隔离、覆盖率统计,以及并发代码的测试纪律。
阅读完本节,你应当能够:
测试文件名以下划线 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 加点号跑全部,加详细标志显示每个用例名。
多个输入输出场景堆 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 注释是断言:输出不符测试失败。它同时被文档工具抓取展示在函数文档旁——文档错了测试就红,一鱼两吃。
第 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 补一个回归用例,是最划算的投入。私有函数怎么测? 同包测试天然可见,直接测即可;若私有逻辑复杂到需要独立测试,通常说明它想被抽成独立类型。测试里能打日志吗? 能,日志函数配详细标志才输出,平时保持安静;断言失败信息要写成"得到什么、想要什么"的对照句式。
下一节盘点弹药库:常用标准库与扩展库。