前言
最近在写 Go 测试时发现官方与社区中有一种大力推崇的惯例:TableDrivenTest,我一直以为测试应该保持简单愚蠢,但这种做法让我想到程序员的三大美德之一:「懒惰」。
什麼是 Table Driven Testing
基本就是把测试会用到的状态存储到 Slice 当中,并用循环统一执行测试:
func Add(a, b int) int { return a + b}
func Test_Add(t *testing.T) { tests := []struct { name string a, b int expected int }{ {"正数相加", 1, 1, 2}, {"加零", 1, 0, 1}, {"负数相加", -1, -1, -2}, }
for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := Add(tt.a, tt.b) if got != tt.expected { t.Errorf("Add(%d, %d) = %d,预期 %d", tt.a, tt.b, got, tt.expected) } }) }}與使用之前比對
普通测试写法每个案例都要复制相同的测试模板:
func Test_Add(t *testing.T) { t.Run("正数相加", func(t *testing.T) { got := Add(1, 1) if got != 2 { t.Errorf("Add(1, 1) = %d,预期 2", got) } })
t.Run("加零", func(t *testing.T) { got := Add(1, 0) if got != 1 { t.Errorf("Add(1, 0) = %d,预期 1", got) } })
t.Run("负数相加", func(t *testing.T) { got := Add(-1, -1) if got != -2 { t.Errorf("Add(-1, -1) = %d,预期 -2", got) } })}每新增一个测试案例就得把测试逻辑再抄一次,而 Table Driven Test 把「测试逻辑」与「测试数据」分离,编辑案例只需要在 slice 里多加一行 struct,测试逻辑本身完全不用动。
总结
我好奇为什么其他语言不太出现这种测试模式,可能是因为这种模式通常只是测试框架包装下的方便手段,而 Go 社区文化下人人对于追求简单程序有一种莫名的执着。
test.each([ ["正数相加", 1, 1, 2], ["加零", 1, 0, 1], ["负数相加", -1, -1, -2],])("%s", (name, a, b, expected) => { expect(add(a, b)).toBe(expected);});