表格驱动测试是Go中通过结构体切片组织测试用例并遍历执行的模式。核心为定义含name、input、want等字段的结构体切片,用t.Run运行子测试,确保用例独立、可读、可维护。

什么是表格驱动测试(table-driven test)
Go 语言中没有内置的「数据驱动」或「参数化测试」语法,但用切片 + 结构体组合就能自然实现表格驱动测试。它的核心是把测试用例组织成 []struct{input, want, name string} 这样的切片,再用 for range 遍历执行——不是靠框架,而是靠 Go 的简洁语法和测试习惯。
如何写一个基础的表格驱动测试
以测试一个字符串首字母大写的函数为例,关键点在于:结构体字段命名清晰、t.Run() 使用子测试名、每个用例独立断言。
func TestCapitalize(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{"empty string", "", ""},
{"single lowercase", "hello", "Hello"},
{"already capitalized", "World", "World"},
{"mixed case", "gOlang", "Golang"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := Capitalize(tt.input); got != tt.want {
t.Errorf("Capitalize(%q) = %q, want %q", tt.input, got, tt.want)
}
})
}
}
-
t.Run()让每个用例在go test -v中显示独立名称,失败时能准确定位 - 结构体字段顺序建议按
name → input → want排列,符合阅读直觉 - 避免在循环内复用变量(如
tt),否则并发子测试可能捕获错误引用(Go 1.21+ 默认启用-race检测)
处理带 error 返回的函数
当被测函数返回 (string, error) 时,表格结构需增加 errWant 字段,并用 errors.Is() 或 assert.Equal(t, err.Error(), ...) 判断错误内容——直接比较 err == nil 不够,因为错误值不支持指针等价判断。
func TestParseID(t *testing.T) {
tests := []struct {
name string
input string
want int
errWant error
}{
{"valid number", "123", 123, nil},
{"empty", "", 0, errors.New("empty ID")},
{"non-digit", "abc", 0, errors.New("invalid digit")},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseID(tt.input)
if !errors.Is(err, tt.errWant) {
t.Errorf("ParseID(%q) error = %v, want %v", tt.input, err, tt.errWant)
return
}
if got != tt.want {
t.Errorf("ParseID(%q) = %d, want %d", tt.input, got, tt.want)
}
})
}
}
- 使用
errors.Is()而非==比较 error,尤其当错误由fmt.Errorf("...")构造时 - 如果
errWant是动态构造的(比如含时间戳),改用strings.Contains(err.Error(), "...") - 子测试里不要提前
return后忽略后续断言;上面示例中先检查 error 再检查结果,是常见且安全的顺序
表格驱动测试容易被忽略的细节
真正影响可维护性的不是写法,而是组织方式和边界意识:
立即学习“go语言免费学习笔记(深入)”;
- 测试数据尽量 inline 在
tests := []struct{...}里,避免拆到全局变量或外部文件——否则无法一眼看清输入输出关系 - 避免在表格里调用函数生成
input或want,例如input: generateRandomString(5),这会让测试不可重现 - 如果某个用例需要特殊 setup(如临时文件、mock HTTP server),把它单独抽成普通测试函数,别硬塞进表格——表格只适合「纯函数式」场景
- 当用例超过 10 条,考虑按行为分组(如
TestParseID_InvalidInput、TestParseID_ValidInput),而不是堆在一个大表格里











