thelper

Table of Contents

  1. 解決する問題
  2. 設定
  3. オプション
  4. サンプル
    1. 検出例
    2. 修正例
  5. 注意点
  6. 参考リンク

thelper はテストヘルパー関数で t.Helper() の呼び出しや引数の一貫性をチェックするツールです。

解決する問題

テストヘルパー関数に t.Helper() がないと、テスト失敗時のエラーメッセージがヘルパー関数内の行を指してしまい、実際にどのテストケースが失敗したのか特定しにくくなります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Before: t.Helper() がない
func assertEqualName(t *testing.T, got, want string) {
// t.Helper() がないため、失敗時にこの行番号が表示される
if got != want {
t.Errorf("name = %q, want %q", got, want) // ← エラー箇所としてここが表示される
}
}

func TestUser(t *testing.T) {
u := NewUser("Alice")
assertEqualName(t, u.Name(), "Bob") // ← 本当はこの行を知りたい
}
// 出力: helper_test.go:5: name = "Alice", want "Bob"
// どのテストから呼ばれたか分からない
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// After: t.Helper() を追加
func assertEqualName(t *testing.T, got, want string) {
t.Helper()
if got != want {
t.Errorf("name = %q, want %q", got, want)
}
}

func TestUser(t *testing.T) {
u := NewUser("Alice")
assertEqualName(t, u.Name(), "Bob")
}
// 出力: user_test.go:4: name = "Alice", want "Bob"
// 呼び出し元の行番号が表示される

設定

公式ドキュメント

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# .golangci.yml 設定例
version: "2"

linters:
enable:
- thelper
settings:
thelper:
test:
first: true
name: true
begin: true
benchmark:
first: true
name: true
begin: true

オプション

test*testing.T)、benchmark*testing.B)、tbtesting.TB)、fuzz*testing.F)の各カテゴリに同じオプションがあります。

オプション デフォルト 説明
first bool true *testing.T 等が最初の引数であることをチェックする
name bool true 引数名が tbtbf)であることをチェックする
begin bool true 関数の先頭で t.Helper() が呼ばれていることをチェックする

サンプル

検出例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// thelper が警告するパターン

// 1. t.Helper() の呼び忘れ
func setupTestDB(t *testing.T) *sql.DB { // test helper function should call t.Helper()
db, err := sql.Open("postgres", testDSN)
if err != nil {
t.Fatal(err)
}
return db
}

// 2. *testing.T が最初の引数でない
func createUser(name string, t *testing.T) { // parameter *testing.T should be the first
t.Helper()
// ...
}

// 3. 引数名が t でない
func deleteUser(tt *testing.T, id string) { // parameter *testing.T should have name t
tt.Helper()
// ...
}

修正例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 全てのチェックを満たすヘルパー関数
func setupTestDB(t *testing.T) *sql.DB {
t.Helper() // 先頭で呼ぶ

db, err := sql.Open("postgres", testDSN)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if err := db.Close(); err != nil {
t.Errorf("close db: %v", err)
}
})
return db
}

// *testing.T が最初の引数、名前は t
func createUser(t *testing.T, name string) *User {
t.Helper()

u, err := NewUser(name)
if err != nil {
t.Fatal(err)
}
return u
}

注意点

  • t.Helper() は関数の最初の文として記述する。条件分岐の後に置くと一部のパスで効果がなくなる
  • ベンチマークヘルパーでは b.Helper() を同様に呼ぶ
  • testing.TB インターフェースを使うと *testing.T*testing.B の両方で使えるヘルパーを作成できる

参考リンク