Go-07-单元测试与 TDD
本节目标:掌握 Go 内置 testing 包的基本用法、表驱动测试、基准测试,理解 TDD 流程。
1. 第一个测试
Go 的测试约定:
- 测试文件以
_test.go结尾 - 测试函数签名:
func TestXxx(t *testing.T) - 在
*_test.go里可以访问包内未导出标识符
1 | // math.go |
运行测试:
1 | go test # 当前包 |
2. Table-Driven Tests(表驱动测试)
Go 社区强烈推崇的测试风格:
1 | func TestAdd(t *testing.T) { |
输出:
1 | === RUN TestAdd |
3. 常用测试工具
3.1 t.Helper() 和 t.Errorf / t.Fatalf
1 | func assertEqual(t *testing.T, got, want interface{}) { |
t.Fatalf vs t.Errorf:
Errorf记录错误继续执行Fatalf记录错误立即停止当前测试
3.2 t.Skip() 跳过条件测试
1 | func TestLinuxOnly(t *testing.T) { |
3.3 子测试与子 subtests
1 | func TestMath(t *testing.T) { |
运行:go test -run TestMath/Add
4. 测试 HTTP 处理器
1 | func helloHandler(w http.ResponseWriter, r *http.Request) { |
测试完整 HTTP 服务:
1 | func TestServer(t *testing.T) { |
5. 基准测试(Benchmark)
1 | func BenchmarkAdd(b *testing.B) { |
运行:
1 | go test -bench=. # 跑所有 benchmark |
输出:
1 | BenchmarkAdd-8 1000000000 0.254 ns/op 0 B/op 0 allocs/op |
6. 示例测试(Example Test)
1 | func ExampleAdd() { |
// Output: 注释让 go test 验证输出。如果改了 Add 但忘了更新示例,测试会失败 —— 文档和测试同步。
7. TDD 流程实战
TDD = Test-Driven Development:先写测试,再写实现。
例:实现一个判断回文字符串的函数。
Step 1(Red):先写测试
1 | // palindrome_test.go |
Step 2:跑测试,看到失败(因为 IsPalindrome 还不存在)
1 | $ go test |
Step 3(Green):写最简单的实现让测试通过
1 | // palindrome.go |
Step 4(Refactor):重构实现 + 测试
测试全过:
1 | $ go test -v |
8. Mock 和依赖注入
1 | type UserStore interface { |
9. testify 库(推荐)
虽然 stdlib 够用,但 testify 让测试更简洁:
1 | import ( |
10. 测试覆盖率
1 | go test -cover |
**Go 1.20+**:可以按包分别看覆盖率
1 | go test -coverprofile=cover.out ./... |
11. 黄金法则
- 测试和代码一起提交(同 PR)
- bug 修复先写测试(重现 bug),再修复
- 不要测第三方库(只测自己的代码)
- 覆盖率不是越高越好(100% 不代表无 bug)
- 测试要快(慢的测试会被跳过)
小结
| 工具 | 用法 |
|---|---|
go test |
跑测试 |
go test -bench |
跑基准 |
go test -cover |
覆盖率 |
t.Run |
子测试 |
t.Helper() |
辅助函数标记 |
t.Skip() |
跳过条件 |
httptest |
HTTP handler 测试 |
testify |
更便捷的断言库 |
TDD 节奏:Red → Green → Refactor,每个循环 5-10 分钟。
下一节讲 Go 的代码组织:包、模块、go.mod。
- 本文作者: CoderSong
- 本文链接: https://jack-song-gif.github.io/2026/08/26/Go-07-单元测试与TDD/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!