testing
パッケージを利用してテストを記述している場合、初期化や終了後の後処理を記述するにはTestMain
関数を利用する。
package test
import (
"log"
"os"
"testing"
)
func TestMain(m *testing.M) {
setup()
code := m.Run()
teardown()
os.Exit(code)
}
func setup() {
// 何らかの初期化処理...
log.Println("setupped here.")
}
func teardown() {
// 何らかの終了処理...
log.Println("cleaned up here.")
}
func TestCase(t *testing.T) {
t.Errorf("the test always fails")
}
この時、いつもの癖でteadown()
をdefer
で呼ぶコードを書いてしまうとする。こんなかんじだ。
func TestMain(m *testing.M) {
setup()
defer teardown()
code := m.Run()
os.Exit(code)
}
しかし、この書き方では問題が起こる。
deferを使ってしまうとこうなる
2015/06/13 00:06:04 setupped here.
=== RUN TestCase
--- FAIL: TestCase (0.00s)
test_test.go:29: the test always fails
FAIL
exit status 1 <--- teardown()が実行されていない!!
FAIL github.com/harukasan/test/test 0.008s
os.Exit(code)
は全てのdeferを破棄して終了する。そのため、deferが正常に実行されずに、後処理が行われないのである。
で、どうするかなんだけど、まず考えるのはdefer
の中でos.Exit(code int)
を呼ぶ方法である。うまいこと終了コードを渡さないといけないので、あまり綺麗じゃないけどポインタ渡しにした。
func TestMain(m *testing.M) {
setup()
var code int
defer func(int *code) {
teardown()
os.Exit(*code)
}(&code)
code = m.Run()
}
他の方法としては、code
をグローバル変数にする方法がある。
var code = 0
func TestMain(m *testing.M) {
setup()
var code int
defer func() {
teardown()
os.Exit(code)
}()
code = m.Run()
}
どっちもどっちっぽいし、そもそもdefer
を使うのをやめた方が綺麗なんじゃないかと思う。