go test を実行すると自動的に go vet が走る
概要¶
go test を実行すると、裏で自動的に go vet が実行されていることを知らなかった、という #golangtokyo での発見の共有。
詳細¶
go vet とは¶
Go の静的解析ツール。コンパイルは通るが、バグになりそうな疑わしいコードを検出する。
検出する問題の例:
// Printf フォーマット文字列の型不一致(コンパイルは通る、でも bug)
fmt.Printf("%d", "hello") // vet が警告: %d expects integer, got string
// unreachable なコード
func f() int {
return 1
return 2 // vet が警告: unreachable code
}
// sync.Mutex のコピー(ポインタ渡しすべきところを値渡し)
type S struct{ mu sync.Mutex }
func use(s S) {} // vet が警告: passes lock by value
go test との関係¶
# これを実行すると...
go test ./...
# 内部でこれが自動的に走っている
go vet ./... # 先にvetを実行
go test ./... # vetが通ったらtestを実行
go test が失敗するケース:
1. go vet で問題が検出された場合(テストコードすら実行されない)
2. テストが失敗した場合
実際の挙動確認¶
// main_test.go
package main
import (
"fmt"
"testing"
)
func TestBad(t *testing.T) {
// go vet で検出される問題
fmt.Printf("%d", "not an int")
t.Log("test")
}
$ go test .
# main [main.test]
./main_test.go:10:17: fmt.Printf format %d has arg "not an int" of wrong type string
FAIL main [build failed]
vet が通らないとテスト自体が実行されない。
vet を無効化する方法(非推奨)¶
通常は無効化せず、vet の警告を修正する。
CI でのベストプラクティス¶
# GitHub Actions
- name: Test
run: go test ./...
# go test は go vet を含むので別途 vet を実行する必要はない
# ただし明示的に実行したい場合
- name: Vet (explicit)
run: go vet ./...
CI で go test を実行していれば go vet も同時に実行されているので二重実行は不要。ただし明示的に書くことでチームメンバーへの周知になる。
なぜ重要か / いつ使うか¶
- Go のテスト環境をセットアップするとき
- CI パイプラインを構築するとき
go testが「ビルドが通るのに落ちる」という状況で原因調査するとき