01. 测试文件和基本结构
Go 有内建的测试框架,不需要接第三方库。测试文件以 _test.go 结尾,测试函数以 Test 开头,放在同包或同目录。用 go test 命令运行,-v 显示详细信息,-run 按正则过滤测试。每个测试函数接收 *testing.T 参数,用来报告失败。
go
// math.go
package math
func Add(a, b int) int { return a + b }
// math_test.go
package math
import "testing"
func TestAdd(t *testing.T) {
result := Add(2, 3)
if result != 5 {
t.Errorf("Add(2,3) = %d; want 5", result)
}
}
// 运行: go test -v测试文件放同包(package xxx)可以访问未导出的函数,做白盒测试。
02. 表驱动测试
表驱动测试是 Go 的惯用测试模式。把测试用例定义成一个结构体切片,每个用例有输入和期望输出。然后用循环遍历执行,失败时用 t.Run 给每个用例起个名,方便定位。这种方式添加新用例非常简单,就是在切片里加一行。
go
func TestAdd(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"正数相加", 2, 3, 5},
{"零加", 0, 5, 5},
{"负数相加", -1, -2, -3},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("got %d, want %d", result, tt.expected)
}
})
}
}t.Run 让每个子用例可以独立运行:go test -run TestAdd/正数相加。
03. Mock 和 test helper
Go 的 mock 不用框架,靠接口实现。定义一个接口,生产代码用真实实现,测试用 mock 实现。辅助函数 t.Helper() 标记后,失败时错误信息指向调用方而不是辅助函数本身,方便定位。临时文件用 t.TempDir(),测试结束自动清理。
go
// 定义接口
type UserService interface {
GetUser(id int) (*User, error)
}
// Mock 实现
type mockUserService struct {
users map[int]*User
}
func (m *mockUserService) GetUser(id int) (*User, error) {
u, ok := m.users[id]
if !ok { return nil, ErrNotFound }
return u, nil
}
// helper 示例
func assertEqual(t *testing.T, got, want interface{}) {
t.Helper() // 关键!
if got != want {
t.Errorf("got %v, want %v", got, want)
}
}Go 社区的 mock 哲学是用接口 + 手写 mock 而不是框架生成,虽然 testify/mock 也常用。
04. 基准测试和 Fuzzing
基准测试函数以 Benchmark 开头,参数是 *testing.B。b.N 是测试框架自动调整的运行次数,运行够多次才能得到稳定的耗时测量。go test -bench . 运行基准测试。Fuzzing(Go 1.18+)用 FuzzXxx 函数,随机扔数据到被测函数看能不全崩溃。
go
// 基准测试
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(100, 200)
}
}
// 运行: go test -bench=.
// Fuzz 测试
func FuzzAdd(f *testing.F) {
f.Add(1, 2) // seed corpus
f.Fuzz(func(t *testing.T, a, b int) {
result := Add(a, b)
if result < a { // 简单的不变式验证
t.Errorf("结果不合理")
}
})
}
// 运行: go test -fuzz=FuzzAdd -fuzztime=10sFuzz 测试可以找到人类想不到的边界情况,非常适合测试解析器、编码器等。
05. 测试覆盖率
go test -cover 显示覆盖率百分比,go test -coverprofile=coverage.out 生成覆盖率文件。然后用 go tool cover -html=coverage.out 在浏览器里打开,绿色是覆盖到,红色是没覆盖到。覆盖率能帮你发现哪些逻辑分支没测到,但不是 100% 覆盖率就等于没 bug。
bash
# 运行测试并生成覆盖率
go test -coverprofile=coverage.out ./...
# 浏览器查看覆盖率详情
go tool cover -html=coverage.out
# 终端查看覆盖率
go tool cover -func=coverage.out
# CI 里检查覆盖率不低于某个值
go test -cover ./... | grep coverage不要为了 100% 覆盖率写没意义的测试。关键业务逻辑和边界条件才是重点。
知识测验
第 1/5 题正确 0
Go 测试文件命名约定是什么?