Dave Cheney had a fantastic post on improving test structure in Go code with table-driven tests. I want to expand on it just a tiny bit to show how table-driven tests can be made more concise with functions.

If you haven’t read his article, read that before continuing.

The end result of Dave’s post is the following example:

func TestSplit(t *testing.T) {
    tests := map[string]struct {
        input string
        sep   string
        want  []string
    }{
        "simple":       {input: "a/b/c", sep: "/", want: []string{"a", "b", "c"}},
        "wrong sep":    {input: "a/b/c", sep: ",", want: []string{"a/b/c"}},
        "no sep":       {input: "abc", sep: "/", want: []string{"abc"}},
        "trailing sep": {input: "a/b/c/", sep: "/", want: []string{"a", "b", "c"}},
    }

    for name, tc := range tests {
        got := Split(tc.input, tc.sep)
        if !reflect.DeepEqual(tc.want, got) {
            t.Fatalf("%s: expected: %v, got: %v", name, tc.want, got)
        }
    }
}

This is a nice way to reduce code duplication, but closing over a new function we can go one step further:

func TestSplit(t *testing.T) {
	test := func(name, input, sep string, want []string) {
		t.Run(name, func(t *testing.T) {
			got := Split(input, sep)
			diff := cmp.Diff(want, got)
			if diff != "" {
				t.Fatalf(diff)
			}
		})
	}

	test("simple", "a/b/c", "/", []string{"a", "b", "c"})
	test("wrong sep", "a/b/c", ",", []string{"a/b/c"})
	test("no sep", "abc", "/", []string{"abc"})
	test("trailing sep", "a/b/c/", "/", []string{"a", "b", "c", ""})
}