テキストや何らかのデータ構造(プログラムを含む)を生成するタイプのコードを書いているときはスナップショットテストが便利で、
- とりあえず一度スナップショットを生成してみる
- 生成されたものを人間が検分する
- 以降はスナップショットが壊れないように・意図したとおりに変更されるようにコードを書いていく
みたいなことをしている。このようにテストスナップショットには、「いったいこのコードで何がアウトプットされるのか?」に対するある種の自動ドキュメンテーションの機能がある。こういうものが欲しくなることは自分の書いたコードに限らずほかにもあって、たとえばクエリビルダのようなブラックボックスをテスト中で用いる際の sanity check 兼ドキュメントとしても有用。
まあ、ドキュメントとして使うことを意識するなら、(それが小さくすむなら)コードの近くにあったほうがありがたい。それでテストスナップショットをコード中に置くようなツールを書こうと思って調べてたら、すでにそういうものがあった。前にも紹介した gotest.tools というやつだ。
これには golden というスナップショットテストの仕組みがあり、testdata/ 以下に置いたファイルとの比較をしてくれるのだけど、それだけではなくわりと一般的に提供される assert.Equal のような関数にも実はスナップショット的に expected を更新してくれる機構がある。こちらはテスト中で値が比較されたとき、expectedXXX と名付けられた変数がその引数に含まれていたら、その変数自身を元のコードで書き換えてしまう、ということをやっている。なかなか大胆。
わかりづらいと思うので実際のコードで説明すると、
func TestBuild_Mutation(t *testing.T) { s, err := Build(...) expectedMutation := `mutation CreateReviewForEpisode($ep: Episode) { createReview(episode: $ep) { stars commentary } }` assert.Equal(t, string(s), expectedMutation) }
以上のコードのうち expectedMutation :=
の文の右辺は go test -update
によって生成されたものだ。こういった感じで、アウトプットが小さい場合や読み手にその場で説明したいような場合にはコード中に埋め込めると便利ですね。
もともとトップレベルの変数の更新しか対応してなかったのを PR 送って関数内の変数にも対応してもらった。まだタグが切られてないので、これを使うにはハッシュを指定して go get する必要がある。文字列以外のデータ構造にも対応できるといいんだけど、そこまでやるなら別ライブラリかな~。