テストヘルパーで testing.TB のオブジェクトを渡していることを保証するためのリンターを作りました。 なお、テストケースを詳細に準備していないので検出漏れがある可能性があります。 github.com
具体的には、次のように一時的に定義されたテストヘルパー内で t.Error() や t.Log() 呼んでいるが、サブテストに紐づくtを参照してない箇所を見つけます。
この間違いによって失敗したサブテストを正しく報告できなくなることに加えて、人によるレビュー以外ではテストが失敗するまでは間違いに気が付きにくいのでリンターで気がつけるようにしてみました。
func Test_a(t *testing.T) { assert := func(a, b int) { if a != b { t.Error("no match") } } t.Run("abcd", func(t *testing.T) { assert(1, 2) // テスト失敗しているが、t を適切に渡せていないので PASS: Test_a/abcd と出力されてしまう }) }
パッケージ直下に作られる関数については特にチェックしていません。 testing.TB オブジェクトをパッケージグローバルに定義して使うようなコードを書くことは、ほぼ無いと考えているためです。
面白かったところ
任意 interface を満たすかどうかをチェックする処理
Go は静的解析を実装するためのツールは揃っていますが、静的解析ツールを実装した経験が少ないと細かな実装方法が直ぐにわからないことがあります。 例えば、インターフェイスを満たしているかのチェック方法をGoogle検索すると Error インターフェイスを満たすことを調べるコード例はでてきますが、任意のパッケージのインターフェイスを満たすかどうかをチェックする方法は簡単には見つけられませんでした。 そのため、GitHub のコード検索でヒットしたコードを参考にして実装しました。
testing.TB interfaceを表す *types.Interface を検索するコード
exttlinter/exttlinter.go at 3f784141436e19f0a007f3853ae0a290695f4cf8 · tomato3713/exttlinter · GitHub
ある識別子が testing.TB interfaceを満たすかのチェックを行うコード exttlinter/exttlinter.go at 3f784141436e19f0a007f3853ae0a290695f4cf8 · tomato3713/exttlinter · GitHub