Kekeの日記

エンジニア、読書なんでも

GoでHTTP APIサーバーのテストを書く

https://fabianlee.org/wp-content/uploads/2017/05/golang-color-icon2.png

本記事

本記事はGolangのHTTP APIサーバーのテストを行なって行こうと思います。

また、うかつにハッカソンで非推奨のGolangのWAFであるIrisを使ってしまい、とんでもなく反省していますが、手法としてはどのフレームワークもだいたい同じなので、この際に包み隠さず記事にしています。

APIサーバーを構築する

以下のようにmain.goはなっています。

func main() {
    app := app.NewIrisApp(db)
    app.Run(iris.Addr(":8880"))
}

そして、以下のように呼び出されています。

func NewIrisApp(db types.Datastorable) *iris.Application {
    stripe.Key = os.Getenv("SECRET_KEY")

    app := iris.Default()

    app.Get("/healthy", func(ctx iris.Context) { ctx.WriteString("Haha! I am healthy.") })

    ctr := handler.NewController(db)

    api := app.Party(fmt.Sprintf("/v%v", types.VERSION))
    api.Post("/payment", ctr.ExecPayment())

    return app
}

つまり以下のようにAPIが設計されています。

Path
/healthy
/v1/payment

テストを書く

以下のようにテストを書きます。試しにGETでは成功してPOSTでは失敗することをチェックしています。

func TestNewIrisApp(t *testing.T) {
    t.Helper()
    t.Run("/healthy", func(t *testing.T) {

        mdb := new(handler.MockDB)
        app := NewIrisApp(mdb)
        e := httptest.New(t, app, httptest.URL("http://payment:8880.com"))

        e.GET("/healthy").WithHeaders(map[string]string{
            "Content-Type": "application/json",
        }).Expect().Status(httptest.StatusOK)

        e.POST("/healthy").WithHeaders(map[string]string{
            "Content-Type": "application/json",
        }).Expect().Status(httptest.StatusNotFound)
    })
}

ポイントとしては

  • httptestを使ってテストをする
  • handlerhttptestに置いてテストをする
  • Expect()Request構造体のメソッドで、*Responceが戻り値である

だけです。非常に簡単にテストをすることができます。

以下のコマンドで実行できます。

go test -v ./...

クエリパラメータをチェックする

例えばクエリパラメータでuserIDが必要なものがあったりすれば、以下のように失敗するかをテストをすることができます。

t.Run("missing userID", func(t *testing.T) {
                mdb := new(handler.MockDB)
                app := NewIrisApp(mdb)
                e := httptest.New(t, app, httptest.URL("http://payment:8880.com"))

                req := e.POST("/v1/payment")
                req.WithQuery("amount", 1000)
                // req.WithQuery("userID", 3) is missing
                req.Expect().Status(httptest.StatusBadRequest)
            })

Bodyをテストする

今まではステータスコードが404なのかなどをチェックしてきました。

ステータスコードが、あっていても、中身があっている保証はありませんのでテストをしていきます。

resp.JSON().Object().ContainsMap(iris.Map{
                    "error": "",
                    "message": iris.Map{
                        "amount":      testAmount,
                        "currency":    stripe.String(string(stripe.CurrencyJPY)),
                        "description": description,
                    },
                })

これはIrisに限らず、適当なMapや構造体を渡すとあっているかチェックしてくれます。

また、ContainsMapの名前からあるように含まれるかをチェックしているので、どんなオブジェクトが返ってくるかわからないものは、無視してもいいかもしれません。

まとめ

特にHTTP APIサーバーのAPIのテストは難しいイメージがありますが、本当に簡単にできるのでぜひやってみてください。

なぜテストをするか、テスト手法に対しては以下の本が参考になります。

レガシーコード改善ガイド (Object Oriented SELECTION)

レガシーコード改善ガイド (Object Oriented SELECTION)

  • 作者: マイケル・C・フェザーズ,ウルシステムズ株式会社,平澤章,越智典子,稲葉信之,田村友彦,小堀真義
  • 出版社/メーカー: 翔泳社
  • 発売日: 2009/07/14
  • メディア: 大型本
  • 購入: 45人 クリック: 673回
  • この商品を含むブログ (157件) を見る

【この1冊でよくわかる】ソフトウェアテストの教科書―品質を決定づけるテスト工程の基本と実践

【この1冊でよくわかる】ソフトウェアテストの教科書―品質を決定づけるテスト工程の基本と実践

テスト駆動開発

テスト駆動開発

初めての自動テスト ―Webシステムのための自動テスト基礎

初めての自動テスト ―Webシステムのための自動テスト基礎