表格驱动测试

Go 项目里非常常见的一种测试写法叫表格驱动测试。

它适合测试多个输入和输出。

一、普通写法的问题

func TestAdd(t *testing.T) {
	if Add(1, 2) != 3 {
		t.Fatal("case 1 failed")
	}
	if Add(2, 3) != 5 {
		t.Fatal("case 2 failed")
	}
	if Add(-1, 1) != 0 {
		t.Fatal("case 3 failed")
	}
}

用例多了以后,可读性会变差。

二、表格驱动写法

func TestAdd(t *testing.T) {
	tests := []struct {
		name string
		a    int
		b    int
		want int
	}{
		{name: "positive", a: 1, b: 2, want: 3},
		{name: "zero", a: -1, b: 1, want: 0},
		{name: "negative", a: -1, b: -2, want: -3},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := Add(tt.a, tt.b)
			if got != tt.want {
				t.Fatalf("Add(%d, %d) = %d, want %d", tt.a, tt.b, got, tt.want)
			}
		})
	}
}

结构:

字段作用
name用例名称
a / b输入
want期望结果

t.Run 会给每个用例创建子测试。

三、测试错误

被测函数:

func Divide(a, b int) (int, error) {
	if b == 0 {
		return 0, errors.New("除数不能为 0")
	}
	return a / b, nil
}

测试:

func TestDivide(t *testing.T) {
	tests := []struct {
		name    string
		a       int
		b       int
		want    int
		wantErr bool
	}{
		{name: "ok", a: 10, b: 2, want: 5, wantErr: false},
		{name: "zero divisor", a: 10, b: 0, want: 0, wantErr: true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := Divide(tt.a, tt.b)

			if tt.wantErr {
				if err == nil {
					t.Fatal("expected error, got nil")
				}
				return
			}

			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			if got != tt.want {
				t.Fatalf("got %d, want %d", got, tt.want)
			}
		})
	}
}

四、适合表格驱动的场景

  • 参数校验
  • 字符串处理
  • 金额计算
  • 状态转换
  • 错误分支
  • API 响应结构转换

表格驱动测试的核心是:把不同场景变成一组清晰的数据。