SuperAPI AI 工具集

go-table-driven-tests

测试与质量

查看项目

更新于 10-16 07:29

技能介绍

用途

该技能用于在 Go 代码库中编写或修改表驱动测试(table-driven tests),确保测试遵循项目既定的模式。当需要编写测试、创建测试函数、添加测试用例,或用户提及相关需求时使用。

核心原则

  • 一个测试函数,多个用例:定义测试用例切片,通过 t.Run() 迭代执行。
  • 显式命名:每个用例都有 name 字段,作为子测试名称。
  • 结构化输入:使用结构体字段承载输入、期望输出和配置。
  • 辅助函数:在测试辅助函数中使用 t.Helper(),以获得正确的行号报告。
  • 环境守卫:当凭证不可用时跳过集成测试。

表结构模式

测试表通常定义为匿名结构体切片,字段包括:name(必需,子测试名称)、input(函数输入)、want(期望输出)、wantErr(期望错误,成功时为 nil)、errCheck(可选,自定义错误校验函数)、setupEnv(可选,环境设置函数,返回清理函数)。随后通过 for 循环配合 t.Run(tt.name, func(t *testing.T) { ... }) 执行每个用例。

字段指南

  • name:必需,子测试名称,应描述具体。
  • input/args:视情况而定,被测函数的输入值。
  • want/want*:视情况而定,期望输出值(如 wantErr、wantResult)。
  • errCheck:可选,自定义错误校验函数。
  • setupEnv:可选,环境设置函数,返回清理函数。

命名约定

  • 测试函数:Test<FunctionName> 或 Test<FunctionName>_<Scenario>。
  • 子测试名称:小写、描述性,允许空格。
  • 输入字段:与参数名一致,或使用 input/args。
  • 输出字段:以 want 为前缀(如 want、wantErr、wantResult)。

常见模式

  1. 基础表测试:定义 name 与输入字段的切片,迭代中调用被测函数并比较结果。
  2. 使用 wantErr 检查错误:用例包含 name、input、wantErr,迭代中使用 errors.Is(err, tt.wantErr) 比较。
  3. 使用 errCheck 自定义错误校验:用例包含 name、setupEnv、wantErr、errCheck,迭代中先执行 setupEnv 并 defer cleanup,再根据 errCheck 校验错误。
  4. 使用 setupEnv 设置环境:用例包含 name、setupEnv、options、wantErr,setupEnv 中设置环境变量并返回清理函数,迭代中 defer 清理后调用被测函数。

集成测试守卫

对于需要真实凭证的测试,使用 skipIfNoCreds 辅助函数:当 TIGRIS_STORAGE_ACCESS_KEY_ID 或 TIGRIS_STORAGE_SECRET_ACCESS_KEY 未设置时调用 t.Skip 跳过测试。该辅助函数内部使用 t.Helper()。

测试辅助函数

在辅助函数中使用 t.Helper() 以获得正确的行号报告。示例包括 setupTestBucket(创建测试桶)和 cleanupTestBucket(删除测试桶),两者均调用 t.Helper(),前者还调用 skipIfNoCreds(t)。

检查清单

编写表驱动测试时:

  • 表结构体的 name 字段放在第一位。
  • 每个测试用例都有描述性名称。
  • 输入字段命名清晰(与参数一致或使用 input)。
  • 期望输出字段以 want 为前缀。
  • 迭代使用 t.Run(tt.name, func(t *testing.T) { ... })。
  • 错误检查使用 errors.Is() 进行比较。
  • 环境设置通过 defer 包含清理。
  • 集成测试使用 skipIfNoCreds(t) 辅助函数。
  • 测试辅助函数使用 t.Helper() 以获得正确的行号报告。
  • 测试文件为 *_test.go,且与被测代码放在一起。

附加最佳实践

  • 详细的错误消息:在错误消息中包含实际值和期望值,便于诊断失败,例如 t.Errorf("got %q, want %q", actual, expected)。注意 t.Errorf 不是断言,记录后测试会继续执行,这有助于判断失败是系统性的还是仅限特定用例。
  • 使用 map 承载测试用例:map 的迭代顺序不确定,可确保测试用例真正相互独立。示例中以字符串为键、结构体为值定义测试表,迭代时使用 for name, tt := range tests 并调用 t.Run(name, ...)。
  • 并行测试:添加 t.Parallel() 调用可并行运行测试用例,循环变量在每次迭代中自动捕获。

参考资料

  • Go Wiki: TableDrivenTests —— Go 社区关于表驱动测试的官方最佳实践。
  • Go Testing Package —— 标准库测试文档。
  • Prefer Table Driven Tests —— Dave Cheney 关于何时以及为何使用表驱动测试而非传统测试结构的指南。