前提
goのhandlerレイヤー、MVCのApplicationで言うところのcontrollerのレイヤーのテストを今まで書いてきたことは少なかったのですが、意図しないinputに対して、正常なレスポンスを返さない(400とか401とかを返す)ようなEPを想定した時にhandlerとは言え、ちゃんとテストを書いておいたほうが良いなと思ったので、今回は普段使っているgin✕GAEの環境でのhandlerのunittestを書く方法を調べました。
summary
- GAE上で動かしているginのApplicationにおいてhandlerのunittestを書きます。
- 使った手順は以下の2つ
- CreateTestContextを使って書く
- testeratorを使って愚直に書く
CreateTestContextを使う
これはテスト用のcontextとginのrouting(engine)を提供してくれるginのテスト用のメソッドです。
refs:
当初はこの CreateTestContext
を使って
// handler.go func Method(c *gin.Context){ // hogehoge } func TestMethod(t *testing.T){ gin.SetMode(gin.TestMode) w := httptest.NewRecorder() _, r := gin.CreateTestContext(w) r.GET("/", Method) req, _ := http.NewRequest("GET", "/", nil) r.ServeHTTP(w, req) assert.Equeal(t, 200, w.Code()) }
というコードを書くことを想定してました。
しかし、1つ罠があって、手元で動かしているginのversionが古すぎて CreateTestContext
が使えないことがわかりました。
ある程度のversionのginであれば CreateTestContext
を使うパターンが最も効率がいいですが、これが使えないので、handlerのテスト毎にそれぞれRoutingを定義してテストを書くという方式を試しました。
testeratorを使って愚直に書く
CreateTestContext
が使えないginのversionにおいては愚直にhandlerごとのテストでroutingを定義してEPに対するテストを書きます。
ただし、ginのcontextをappengineのcontextから生成するのに一工夫必要で今回は testerator
というappengineのテスト高速化のライブラリの力を借りました。
これはappengineのプロセス(instance)を高速に立ち上げてくれるテスト用のライブラリです。
refs
testeratorを使うときの注意点は SpinUp
した後テストが完了したら SpinDown
してプロセスを明示的に完了させる必要があります。
終了させないと延々appengineのプロセスが立ち上がり続けて、ciが落ちるかもしれません。
// handler.go func Method (c *gin.Context){ // hogehoge } func TestMethod(t *testing.T){ gin.SetMode(gin.TestMode) instance, _, _ := testerator.SpinUp() defer testerator.SpinDown() r := gin.New() r.GET("/", Method) req, _ := instance.NewRequest("GET", "/", nil) w := httptest.NewRecorder() r.ServeHTTP(w, req) assert.Equal(t, 200, w.Code()) }
これでテストをやってみると
$ goapp run -v ./ -run TestMethod appengine: not running under devappserver2; using some default configuration === RUN TestMethod # 略 --- PASS: TestMethod Pass ok ./handler
このようにテストが通ります。
まとめ
普段requestやhandlerのテストを書くことは少ないですが、inputとそれに対して返ってくるレスポンスが期待したレスポンスになっているのかはテストとして残しておいたほうが良いと思いました。