mcpField
G
go-testing

go-testing

github.com/o-marsters-1997测试与质量062 次安装3 天前更新
代码仓库

关于 go-testing

为不同代码库提供统一 Go 测试规范的技能,面向编写或维护 Go 代码的 Agent。内容涵盖测试结构、替身选择、数据库测试及验证流程。

主要功能

  1. 01通过导出 API 编写黑盒测试
  2. 02优先使用有状态 Fake
  3. 03使用真实 Postgres 做集成测试
  4. 04异步与模糊测试指南
  5. 05测试和 lint 命令

适用场景

  1. 01为 Go 功能补充测试
  2. 02测试 SQL 驱动的行为
  3. 03检查并发 Go 代码
  4. 04审查 Go 测试套件

安装

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

$npx skills add https://github.com/o-marsters-1997/my-claude-code --skill go-testing

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

包含的文件

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

assets/
golangci-depguard.yml
assets/pgtest/
pgtest_test.go
pgtest.go
settings.go
sweep_test.go
evals/
evals.json
references/
examples.md
integration-db.md

详细介绍

Go 测试

介绍如何在每个代码库中编写 Go 测试。测试哪些内容,以代码库自带的 testing-policy 为准(如果有):它使用哪些类型的测试、每种测试的价值、功能需要哪些测试,以及随着代码库发展可复用的模式。它绝不重述如何编写测试,因此两者不会重叠。红—绿—重构循环请使用 tdd。

这里不适用的默认做法

这些习惯常常会不假思索地冒出来。写测试前先对照此表检查,完成前再检查一次,因为任务耗时较长时,它们很容易在后期重新出现。

你会采用的做法改用这种做法
testify assert/require,或任何断言库使用普通的 if 加 t.Errorf;非标量值使用 cmp.Diff
reflect.DeepEqual使用 cmp.Diff(want, got),并以 (-want +got) 的形式报告
使用 wantErr bool 和 if tt.wantErr 的表格在一个测试中使用两张表:一张测试成功情况,另一张使用 errors.Is 检查错误
gomock、mockery、EXPECT().Times(1)使用有状态的内存 fake;断言其最终状态
sqlmock 或模拟的 database/sql 驱动使用 internal/pgtest 中的真实 Postgres
用 TestMain 在每个包启动一个容器使用 pgtest.New(t):每个代码库一个容器,每个测试一个数据库
在测试之间使用 TRUNCATE,共享行每个测试使用一个全新的数据库,以确保 t.Parallel() 安全
使用 time.Sleep 等待 goroutine使用代码发出的信号通道、截止时间轮询,或 testing/synctest
使用 strings.Contains(err.Error(), "...")针对导出的 sentinel 或类型使用 errors.Is / errors.As
新增测试依赖只使用 github.com/google/go-cmp;未经询问,不添加其他依赖

规则

  1. 通过导出的 API,在黑盒 package foo_test 中测试行为。只有当单元按设计未导出时才使用 package foo,并将 export_test.go 作为最后手段。
  2. 断言输出和状态,而不是断言调用了哪些函数。若重构未改变行为却会导致测试失败,这种测试就是变更检测器:删除它。
  3. 选择最忠实的替身:真实实现,其次是仿真对象,再其次是桩对象,最后才是 Mock。只对跨系统边界的状态变更调用(发送邮件、扣款)使用 Mock,并且要手写记录器,不要用 Mock 库。
  4. 仿真对象有状态且行为真实:使用相同的哨兵值,表现出相同的未找到行为。一套契约测试同时针对仿真对象和真实实现运行。要强制触发错误路径时,使用只实现一个方法并嵌入接口的桩对象,绝不要在仿真对象上设置故障开关。
  5. 使用 internal/pgtest,针对真实的 Postgres 测试 SQL。如果仓库没有 internal/pgtest,请从此技能的资源中安装:加载 references/integration-db.md。
  6. 使用 cmp.Diff(want, got) 进行比较,并报告 (-want +got)。如果 go.mod 中没有 github.com/google/go-cmp,就在首次修改测试时使用 go get 添加它,并在同一次修改中修正规则与之矛盾的 lint 规则或文档。如果用户或仓库明确禁止添加依赖(“仅使用标准库”),则用 == 比较标量,用 slices.Equal/maps.Equal 比较切片和映射,并逐字段比较结构体。无论哪种情况,都不要使用 reflect.DeepEqual。
  7. 失败消息格式为 Func(in) = got, want want。使用 errors.Is/errors.As 匹配错误;需要区分错误的调用方应获得一个导出的哨兵值或类型。
  8. 对每个待测函数或方法编写一个外层测试(TestFunc、TestType_Method);每个用例都在 t.Run 中运行。若用例使用相同方式检查,则使用表格:由带有 name 字段的结构体组成的切片,并通过 t.Run(tt.name, ...) 运行。若检查方式不同,则在同一个外层测试中编写具名子测试。按用例字段分支时,应编写具名子测试,而不是使用一个表格。绝不要为某个场景另写一个顶层 Test。
  9. 使用 t.Error 继续执行;只有在后续步骤无法运行时才使用 t.Fatal。绝不要在测试未启动的 goroutine 中调用 t.Fatal;应通过 channel 传回错误。
  10. 辅助函数接收 t,调用 t.Helper(),遇到设置错误时使测试失败,并注册 t.Cleanup。辅助函数负责设置,不负责断言。不要编写通用的 assertEqual。
  11. 不要使用 time.Sleep 等待。应等待 channel、在截止时间前轮询条件,或在涉及计时器时使用 synctest.Test(Go 1.25+)。
  12. 只有在测试不共享任何状态时才调用 t.Parallel():不得使用全局变量、t.Setenv、t.Chdir 或共享行。pgtest 数据库绝不共享,因此数据库测试可以并行运行。
  13. 比较 JSON 时,先解码 JSON,绝不要按字节比较。比较前规范化 golden 输出;每个 -update 差异都必须由人工审核。
  14. 使用属性(不发生 panic、往返转换)对不可信输入的解析器进行模糊测试;提交 testdata/fuzz/。
  15. 通过真实路由器和中间件测试 HTTP 处理器,构建方式应与 main 中一致。

运行

直接运行 go test ./... 即可执行所有测试,包括数据库测试:不使用构建标签、不使用 -short 跳过测试,也不通过 make 目标运行。构建标签仅用于端到端测试。如果没有 Docker,且未设置 TEST_DATABASE_URL,pgtest 会失败并给出同时提及两者的提示;不要用 t.Skip 掩盖问题。完成前运行 go test -race ./... 和 golangci-lint run。

查阅位置

如果你正在编写……请加载
涉及 Postgres 的测试,或尚无 internal/pgtest 的代码仓库references/integration-db.md
数据库测试的 CI(TEST_DATABASE_URL、服务容器)references/integration-db.md
单元、表驱动、fake 加契约套件、HTTP、异步、黄金文件、模糊测试或基准测试references/examples.md

assets/pgtest/ 存放辅助工具及其自身的测试;assets/golangci-depguard.yml 存放一条 lint 规则,用于确保 testcontainers 的导入仅限于 internal/pgtest。

另请参阅

技能适用情况
go-idiomatic被测代码的所有相关内容
tdd先编写测试,一次处理一个行为
testing-policy测试哪些内容:测试类型、测试价值、可复用模式(按代码仓库划分)

评论

支持 Markdown