経験は何よりも饒舌

10年後に真価を発揮するかもしれないブログ 

自分のPRを見て思う、TableDrivenTestsの良さ

jdという、commandline utility and Go library for diffing and patching JSON valuesなライブラリにTableDrivenTestsを導入したので、その良さを主観的にまとめておこうと思う。

github.com

github.com


まずは、実際にdiffの一部を見てみる。
https://github.com/josephburnett/jd/pull/29/files#diff-a0a78ab7b3426f60e7b758b565129777bdcef35e95a490999a0ff377389576d1R83

before

ctx := newTestContext(t)
checkDiff(ctx, `[]`, `[]`)
checkDiff(ctx, `[[]]`, `[[1]]`,
	`@ [0,0]`,
	`+ 1`)
checkDiff(ctx, `[1,2,3]`, `[1,null,3]`,
	`@ [1]`,
	`- 2`,
	`+ null`)
checkDiff(ctx, `[]`, `[3,4,5]`,
	`@ [0]`,
	`+ 3`,
	`@ [1]`,
	`+ 4`,
	`@ [2]`,
	`+ 5`)

after

ctx := newTestContext(t)
tests := []struct {
	context   *testContext
	a         string
	b         string
	diffLines []string
}{
	{ctx, `[]`, `[]`, []string {}},
        {ctx, `[[]]`, `[[1]]`, []string {
	     `@ [0,0]`,
	     `+ 1`,
	  },
        },
	{ctx, `[1,2,3]`, `[1,null,3]`, []string {
	    `@ [1]`,
	     `- 2`,
            `+ null`,
           },
        },
        {ctx, `[]`, `[3,4,5]`, []string {
            `@ [0]`,
	    `+ 3`,
            `@ [1]`,
            `+ 4`,
            `@ [2]`,
            `+ 5`,
	   },
	},
}
for _, tt := range tests {
     checkDiff(tt.context, tt.a, tt.b, tt.diffLines...)
}

よいと思うところを2つ書く。

関数呼び出しが冗長でなくなった

beforeafterで関数を呼び出す回数は変わらないが、afterはループの中で呼び出しているので、パッと見て冗長な表現にはなっていない。
func TestArrayDiff(t *testing.T) {のなかのテストなので、checkDiffを何回も呼び出している(ように見える)のは多少くどい。

関数の引数が宣言的になった

checkDiff関数は、func checkDiff(ctx *testContext, a, b string, diffLines ...string) {という形で引数を取る。
jd/common_test.go at d1d4edfee5375f88c1155ef55158597fb8488751 · josephburnett/jd · GitHub

beforeでは、どこからがdiffLinesが多少わかりにくくなっているが、afterでは、[]stringでまとめているため、checkDiffに何がどの引数で渡されるのかが分かりやすくなっている。

{
	context   *testContext
	a         string
	b         string
	diffLines []string
}

また、上のように、テストのはじめに宣言できることで、可読性が非常に高くなっている。
今回のようにbeforeがあの状態だったら、TableDrivenTestを導入しない手はないのではないかと思う。

割といい変更がだせたので自画自賛タイトルをつけてみた。