如何使用Golang subtest组织测试_分组执行相关测试用例

Go 1.7 引入的 t.Run() 是组织测试用例最自然、最推荐的方式,支持逻辑分组、嵌套层级、独立生命周期控制(如跳过、清理、并行)、精准命令行执行及清晰输出。

Go 1.7 引入的 t.Run() 是组织和分组测试用例最自然、最推荐的方式。它让测试结构更清晰、输出更易读,还能独立控制每个子测试的生命周期(如设置/清理、跳过、超时)。

用 t.Run() 创建逻辑分组

在单个测试函数中,通过多次调用 t.Run(name, func(t *testing.T)) 定义多个子测试。每个子测试有独立名称,运行时会显示为 TestXxx/SubtestName 形式。

例如验证字符串处理函数的不同场景:

func TestParseDuration(t *testing.T) {
    tests := []struct {
        name     string
        input    string
        expected time.Duration
        wantErr  bool
    }{
        {"zero", "0s", 0, false},
        {"seconds", "30s", 30 * time.Second, false},
        {"invalid", "1y", 0, true},
    }
    for _, tt := range tests {
        tt := tt // 避免循环变量捕获问题
        t.Run(tt.name, func(t *testing.T) {
            d, err := time.ParseDuration(tt.input)
            if (err != nil) != tt.wantErr {
                t.Fatalf("ParseDuration(%q) error = %v, wantErr %v", tt.input, err, tt.wantErr)
            }
            if !tt.wantErr && d != tt.expected {
                t.Errorf("ParseDuration(%q) = %v, want %v", tt.input, d, tt.expected)
            }
        })
    }
}

支持嵌套子测试与层级结构

子测试可以递归调用 t.Run(),形成多级分组。适合按功能模块 + 场景进一步拆解:

立即学习“go语言免费学习笔记(深入)”;

  • 顶层测试函数代表一个大功能(如 TestAuth
  • 第一层子测试按流程分(如 "Login", "Logout", "RefreshToken"
  • 第二层可按输入类型或错误路径细分(如 "Login/ValidCredentials", "Login/EmptyPassword"

运行时可通过 go test -run=TestAuth/Login/ValidCredentials 精准执行某条路径。

利用子测试特性做隔离与控制

每个子测试拥有独立的 *testing.T 实例,因此可安全使用以下方法而不影响其他子测试:

  • t.Skip()t.Skipf():跳过当前子测试(比如只在 CI 中运行的集成测试)
  • t.Cleanup(func()):注册清理函数,子测试结束时自动执行(比 defer 更可靠)
  • t.Setenv(key, value):临时修改环境变量,退出子测试后自动还原
  • t.Parallel():标记子测试可并行执行(注意共享状态需加锁或避免)

配合 go test 命令精准执行

子测试名称支持通配符和路径匹配,大幅提升调试效率:

  • go test -run=TestParseDuration/seconds —— 运行指定子测试
  • go test -run=TestParseDuration/invalid —— 快速复现错误分支
  • go test -run=^TestAuth/Login$ —— 用正则匹配完整名称(需转义 ^$)
  • go test -v —— 查看详细子测试执行顺序和耗时

输出示例:

--- PASS: TestParseDuration (0.00s)
--- PASS: TestParseDuration/zero (0.00s)
--- PASS: TestParseDuration/seconds (0.00s)
--- PASS: TestParseDuration/invalid (0.00s)