Go言語のテストケースの一覧性を高める 

Goのテーブル駆動テストは、記述を工夫することで一覧性を高めることができます。

[strings.SplitN](https://pkg.go.dev/strings#SplitN)関数のテストを例に説明します。
# 通常の実装
シンプルなので読みにくくはないですが、行数の割に情報量が少なく全体を把握するにはスクロールが必要です。
```golang
func TestSplitN_A(t *testing.T) {
	type args struct {
		s   string
		sep string
		n   int
	}
	tests := []struct {
		name string
		args args
		want []string
	}{
		{
			name: "-1",
			args: args{
				"A,B,C,D",
				",",
				-1,
			},
			want: []string{"A", "B", "C", "D"},
		},
		{
			name: "0",
			args: args{
				"A,B,C,D",
				",",
				0,
			},
			want: nil,
		},
		{
			name: "1",
			args: args{
				"A,B,C,D",
				",",
				1,
			},
			want: []string{"A,B,C,D"},
		},
		{
			name: "2",
			args: args{
				"A,B,C,D",
				",",
				2,
			},
			want: []string{"A", "B,C,D"},
		},
		{
			name: "3",
			args: args{
				"A,B,C,D",
				",",
				3,
			},
			want: []string{"A", "B", "C,D"},
		},
		{
			name: "4",
			args: args{
				"A,B,C,D",
				",",
				4,
			},
			want: []string{"A", "B", "C", "D"},
		},
		{
			name: "empty_string_0",
			args: args{
				"",
				",",
				0,
			},
			want: nil,
		},
		{
			name: "empty_string_1",
			args: args{
				"",
				",",
				1,
			},
			want: []string{""},
		},
		{
			name: "empty_string_2",
			args: args{
				"",
				",",
				2,
			},
			want: []string{""},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := strings.SplitN(tt.args.s, tt.args.sep, tt.args.n); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("strings.SplitN() = %v, want %v", got, tt.want)
			}
		})
	}
}

```

# 改善1：改行をなくす
改行をなくして1行1テストケースにしました。
スクロールしなくても全体が把握でき、テストケース毎の違いも見やすくなりました。
```golang
func TestSplitN_B(t *testing.T) {
	type args struct {
		s   string
		sep string
		n   int
	}
	tests := []struct {
		name string
		args args
		want []string
	}{
		// ↓↓↓　変更部分ここから　↓↓↓
		{name: "-1", args: args{"A,B,C,D", ",", -1}, want: []string{"A", "B", "C", "D"}},
		{name: "0", args: args{"A,B,C,D", ",", 0}, want: nil},
		{name: "1", args: args{"A,B,C,D", ",", 1}, want: []string{"A,B,C,D"}},
		{name: "2", args: args{"A,B,C,D", ",", 2}, want: []string{"A", "B,C,D"}},
		{name: "3", args: args{"A,B,C,D", ",", 3}, want: []string{"A", "B", "C,D"}},
		{name: "4", args: args{"A,B,C,D", ",", 4}, want: []string{"A", "B", "C", "D"}},
		{name: "empty_string_0", args: args{"", ",", 0}, want: nil},
		{name: "empty_string_1", args: args{"", ",", 1}, want: []string{""}},
		{name: "empty_string_2", args: args{"", ",", 2}, want: []string{""}},
		// ↑↑↑　変更部分ここまで　↑↑↑
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := strings.SplitN(tt.args.s, tt.args.sep, tt.args.n); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("strings.SplitN() = %v, want %v", got, tt.want)
			}
		})
	}
}
```

# 改善2：`/*　*/`コメントを駆使して列を揃える
列を揃えることでテストケース毎の変数の違いがより分かりやすくなるかと思います。
ただ、これは手間が掛かるので、必要に応じて使うのをおすすめします。
```golang
func TestSplitN_C(t *testing.T) {
	type args struct {
		s   string
		sep string
		n   int
	}
	tests := []struct {
		name string
		args args
		want []string
	}{
		// ↓↓↓　変更部分ここから　↓↓↓
		{name: "-1" /*       */, args: args{"A,B,C,D", ",", -1}, want: []string{"A", "B", "C", "D"}},
		{name: "0" /*        */, args: args{"A,B,C,D", ",", 0}, want: nil},
		{name: "1" /*        */, args: args{"A,B,C,D", ",", 1}, want: []string{"A,B,C,D"}},
		{name: "2" /*        */, args: args{"A,B,C,D", ",", 2}, want: []string{"A", "B,C,D"}},
		{name: "3" /*        */, args: args{"A,B,C,D", ",", 3}, want: []string{"A", "B", "C,D"}},
		{name: "4" /*        */, args: args{"A,B,C,D", ",", 4}, want: []string{"A", "B", "C", "D"}},
		{name: "empty_string_0", args: args{"" /*   */, ",", 0}, want: nil},
		{name: "empty_string_1", args: args{"" /*   */, ",", 1}, want: []string{""}},
		{name: "empty_string_2", args: args{"" /*   */, ",", 2}, want: []string{""}},
		// ↑↑↑　変更部分ここまで　↑↑↑
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := strings.SplitN(tt.args.s, tt.args.sep, tt.args.n); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("strings.SplitN() = %v, want %v", got, tt.want)
			}
		})
	}
}
```

# 改善3: ヘルパー関数を用意する
今回のケースではあまり有用ではないですが、ヘルパー関数を使うことで冗長な記述がなくなり見やすくなります。
```golang
func TestSplitN_D(t *testing.T) {
	type args struct {
		s   string
		sep string
		n   int
	}
	// ↓↓↓　変更部分ここから　↓↓↓
	type test struct {
		name string
		args args
		want []string
	}
	makeTest := func(name string, args args, want []string) test {
		return test{name: name, args: args, want: want}
	}
	tests := []test{
		makeTest("-1" /*       */, args{"A,B,C,D", ",", -1}, []string{"A", "B", "C", "D"}),
		makeTest("0" /*        */, args{"A,B,C,D", ",", 0}, nil),
		makeTest("1" /*        */, args{"A,B,C,D", ",", 1}, []string{"A,B,C,D"}),
		makeTest("2" /*        */, args{"A,B,C,D", ",", 2}, []string{"A", "B,C,D"}),
		makeTest("3" /*        */, args{"A,B,C,D", ",", 3}, []string{"A", "B", "C,D"}),
		makeTest("4" /*        */, args{"A,B,C,D", ",", 4}, []string{"A", "B", "C", "D"}),
		makeTest("empty_string_0", args{"" /*   */, ",", 0}, nil),
		makeTest("empty_string_1", args{"" /*   */, ",", 1}, []string{""}),
		makeTest("empty_string_2", args{"" /*   */, ",", 2}, []string{""}),
	}
	// ↑↑↑　変更部分ここまで　↑↑↑
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if got := strings.SplitN(tt.args.s, tt.args.sep, tt.args.n); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("strings.SplitN() = %v, want %v", got, tt.want)
			}
		})
	}
}
```

# まとめ
テストコードは分量が多くなるので読む量も増えますが、こういったちょっとした工夫で読みやすくなり、間違いや漏れなどにも気づきやすくなるかと思います。

今回例に上げたソースコードはこちらに置いてます。
https://github.com/fujiwaram/splitn-test