emahiro/b.log

Drastically Repeat Yourself !!!!

gin✕GAEの環境でhandlerのunittestを書く

前提

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:

godoc.org

github.com

当初はこの 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

github.com

godoc.org

qiita.com

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とそれに対して返ってくるレスポンスが期待したレスポンスになっているのかはテストとして残しておいたほうが良いと思いました。