关于 go-testing
面向 Go 开发者的测试技能,指导测试编写与组织,涵盖测试模式、辅助函数、断言和集成测试,并提供脚手架生成脚本与模板。
主要功能
- 01表驱动测试与子测试指南
- 02测试辅助函数、夹具和清理模式
- 03包含输入、实际值与期望值的失败信息
- 04语义化错误检查与比较建议
- 05脚手架脚本和表驱动测试模板
适用场景
- 01创建表驱动测试用例
- 02编写带可靠清理逻辑的测试辅助函数
- 03测试 HTTP 处理程序、数据库或外部服务
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ cxuu/ golang-skills --skill go-testing安装后在 Claude Code 中输入/go-testing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/table-test-template.goreferences/INTEGRATION.mdTABLE-DRIVEN-TESTS.mdTEST-HELPERS.mdTEST-ORGANIZATION.mdVALIDATION-APIS.mdscripts/gen-table-test.sh详细介绍
Go 测试
兼容性:Diff 示例可能会使用
github.com/google/go-cmp。
资源路由
scripts/gen-table-test.sh- 生成表驱动测试脚手架时运行。assets/table-test-template.go- 将其用作可复制的表驱动测试起始模板。references/TABLE-DRIVEN-TESTS.md- 选择表驱动测试、子测试或并行测试模式时阅读。references/TEST-HELPERS.md- 编写辅助函数、测试固件、清理代码或测试替身时阅读。references/TEST-ORGANIZATION.md- 组织包、黑盒测试或较大型测试套件时阅读。references/VALIDATION-APIS.md- 选择t.Error、t.Fatal、cmp.Diff或断言风格时阅读。references/INTEGRATION.md- 测试外部服务、HTTP 处理程序、数据库或长时间运行的初始化流程时阅读。
快速参考
| 模式 | 使用时机 |
|---|---|
t.Error | 默认选择——报告失败并继续运行 |
t.Fatal | 初始化失败,或继续执行已无意义 |
cmp.Diff | 比较结构体、切片、映射、原型 |
| 表驱动测试 | 多个用例共用相同逻辑 |
| 子测试 | 需要筛选、并行执行或命名 |
t.Helper() | 任何测试辅助函数(作为第一条语句调用) |
t.Cleanup() | 在辅助函数中清理,而不是使用 defer |
实用的测试失败信息
规范性要求:无需阅读测试源代码也必须能够诊断测试失败。
每条失败信息都必须包含:函数名、输入、实际值(got)和预期值(want)。使用格式 YourFunc(%v) = %v, want %v。
// Good:
t.Errorf("Add(2, 3) = %d, want %d", got, 5)
// Bad: Missing function name and inputs
t.Errorf("got %d, want %d", got, 5)
始终先打印 got,再打印 want:got %v, want %v——顺序不得颠倒。
不使用断言库
规范性要求:不要使用断言库。复杂比较请使用
cmp.Diff。
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("GetPost() mismatch (-want +got):\n%s", diff)
}
对于 protocol buffer,请添加 protocmp.Transform() 作为 cmp 选项。Diff 信息中始终包含方向键 (-want +got)。避免比较 JSON/序列化输出——应改为比较语义。
t.Error 与 t.Fatal
规范性要求:默认使用
t.Error,在一次运行中报告所有失败。只有在无法继续执行时才使用t.Fatal。
以下情况选择 t.Fatal:
- 初始化失败(数据库连接、文件加载)
- 后续断言依赖前一项成功(例如,编码后再解码)
除测试 goroutine 外,绝不要在其他 goroutine 中调用 t.Fatal/t.FailNow——请改用 t.Error。
表驱动测试
新建表驱动测试脚手架并需要标准结构体、循环和子测试布局时,请参阅
assets/table-test-template.go。
建议:多个用例共用相同逻辑时,使用表驱动测试。
**适合使用表驱动测试的情况:**所有用例都运行相同的代码路径,不需要条件式初始化、模拟或断言。可以使用一个 shouldErr 布尔值。
**不适合使用表驱动测试的情况:**用例需要复杂初始化、条件式模拟或多个分支——请改为编写单独的测试函数。
关键规则:
- 用例跨多行或相邻字段类型相同时,请使用字段名
- 在失败信息中包含输入——绝不要通过索引标识行
验证:生成或修改测试后,运行
go test -run TestXxx -v,验证测试能够编译并通过。继续之前,请修复所有编译错误。
测试辅助函数
规范性要求:测试辅助函数必须先调用
t.Helper(),并使用t.Cleanup()执行清理。
func setupTestDB(t *testing.T) *sql.DB {
t.Helper()
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatalf("Could not open database: %v", err)
}
t.Cleanup(func() { db.Close() })
return db
}
测试错误语义
建议:测试错误语义,而不是错误消息字符串。
// Bad: Brittle string comparison
if err.Error() != "invalid input" { ... }
// Good: Semantic check
if !errors.Is(err, ErrInvalidInput) { ... }
对于不关心具体语义的简单存在性检查:
if gotErr := err != nil; gotErr != tt.wantErr {
t.Errorf("f(%v) error = %v, want error presence = %t", tt.input, err, tt.wantErr)
}
相关技能
- 错误测试:测试错误语义时,如使用
errors.Is/errors.As或哨兵错误,请参阅 go-error-handling - 接口模拟:在使用者一侧通过实现接口来创建测试替身时,请参阅 go-interfaces
- 测试函数命名:为测试函数、子测试或测试辅助工具命名时,请参阅 go-naming
- Linter 集成:在 CI 或提交前钩子中与测试一同运行 linter 时,请参阅 go-linting