Goで外部プログラムをexecするpackageのテストをどうするか
とsongmuさんに教えてもらったことを参考に考えてやってみた方法を紹介します。あくまでも自分が考えた方法です。もっといい方法などあればぜひ教えてください。
まずテストの実行前に常に実行する処理を書きたいです。これは*testing.M
を使えばできます。
こう書くことでテストの実行前に setup
関数、全てのテスト終了時に clean
関数を実行できます。tmpのディレクトリ名を渡しているのはあとで解説します。それでは setup
関数と clean
関数はどうすればいいのかという話になりますが、その前にどういうテストを実行すべきかを考えてみます。
今回は外部プログラムの代わりに違うプログラムを実行したいのでそのプログラムを setup
関数で準備します。準備するプログラムは以下のようにしました。
起動したプロセスが突然死んだり、SIGTERMを送ってもなかなか死なないケースのパターンをテストしたかったので以下のようなオプションに対応したプログラムを起動するようにしました。
-survival-time
で起動してから死ぬまでの時間-wait-time
でSIGTERM/SIGINT
を送られてから死ぬまでの時間-exit-code
でExit時のステータスコード
このプログラムを保存するファイルの拡張子を .go
にしてしまうとmainパッケージでないディレクトリの中にmainパッケージのmain関数が存在することになり、色々とおかしなことになります。なので別の拡張子にしてテスト内でファイルを読み込むようにするか、文字列で定義します。今回は文字列で定義するようにします。
先程紹介したプログラムを文字列で持ちつつ、tmpのディレクトリの中にファイルとして保存します。その後に go build
することで今回のバイナリを用意することができます。バイナリの位置を programBinary
に代入しているので、各テストで適切なオプションを与えながら実行することで今回のお目当てのpackageのテストができるようになります。テストが終わったら最後に clean
関数で作成したファイルを削除します。
おまけ
What version of Go are you using (go version)? go version go1.8.3 linux/amd64 Currently, process started from exec…github.com
ソースコードを読んでいて気付いたのですが、 exec.CommandContext
で context
経由で cancel
を呼ぶとプロセスに(問答無用でいきなり) SIGKILL
が送られます。またまたご冗談を、って感じですが本当です。
SIGKILLを送られて大丈夫なケースってあまり思いつかないので、使えるケースはほぼないのではないでしょうか。とりあえず安易に使わないようにしましょう。