mcpField
G
go-testing

go-testing

github.com/cxuu测试与质量1861.4k 次安装19 周前更新
代码仓库

关于 go-testing

面向 Go 开发者的测试技能,指导测试编写与组织,涵盖测试模式、辅助函数、断言和集成测试,并提供脚手架生成脚本与模板。

主要功能

  1. 01表驱动测试与子测试指南
  2. 02测试辅助函数、夹具和清理模式
  3. 03包含输入、实际值与期望值的失败信息
  4. 04语义化错误检查与比较建议
  5. 05脚手架脚本和表驱动测试模板

适用场景

  1. 01创建表驱动测试用例
  2. 02编写带可靠清理逻辑的测试辅助函数
  3. 03测试 HTTP 处理程序、数据库或外部服务

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/cxuu/golang-skills --skill go-testing

安装后在 Claude Code 中输入/go-testing确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/
table-test-template.go
references/
INTEGRATION.md
TABLE-DRIVEN-TESTS.md
TEST-HELPERS.md
TEST-ORGANIZATION.md
VALIDATION-APIS.md
scripts/
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

评论

支持 Markdown