STORES Product Blog

こだわりを持ったお商売を支える「STORES」のテクノロジー部門のメンバーによるブログです。

net/http/httptest でHTTPクライアントをテストする方法

はじめに

こんにちは、STORESの高田です。

今回は net/http/httptest でHTTPクライアントをテストする方法についてご紹介します。

外部サービスのクライアントを含めたテストを実装する際には net/http/httptest を使うと、外部依存を排除しつつ再現性のあるテストを書くことができます。

基本的な使い方

テストの一例として、 Refresh token を用いて Access token を取得する箇所での使い方を見ていきます。

使い方は以下の通りで、httptest.NewServer でテスト用のHTTPサーバを作成します。func NewServer(handler http.Handler) *Server と定義されているので、モックしたい内容を http.Handler interface に沿った形で実装します。以下の例では、refresh_token のプレフィックスが success-refresh-tokenである場合に、access_token を含むJSONをレスポンスとして返します。

func Test_GetAccessToken(t *testing.T) {
    ...
    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if err := r.ParseForm(); err != nil {
            fmt.Fprintf(w, "%#v", err)
        }
    
        if r.URL.Path == "/token" {
            refreshToken := r.Form.Get("refresh_token")
            if strings.HasPrefix(refreshToken, "success-refresh-token") {
                res := `{"access_token": "new-access-token-` + refreshToken + `", "expires_in": 3920, "scope": "fooScope", "token_type": "Bearer" }`
                w.Header().Set("Content-Type", "application/json")
                w.WriteHeader(http.StatusOK)
                fmt.Fprint(w, res)
            } else {
                fmt.Fprintf(w, "hello")
            }
            return
        }
        
        http.NotFound(w, r)
    }))
    defer server.Close()
    
    ...
    
    app.Client.TokenURL = fmt.Sprintf("%s/token", server.URL) // クライアントのリクエスト先をモックサーバに置き換える

    ...
    
    res, err := app.GetAccessToken(ctx, refreshToken) // 通常通り呼び出す(リクエスト先はモックサーバとなる)
    if err != nil {
        t.Errorf("GetAccessToken() error = %v", err)
    }
    
    ...

※都合によりコードの抜粋となっています。詳細は httptest package - net/http/httptest - Go Packages をご参照ください。

http.Handler interface を実装すれば良いので、共通のロジックを持つモックサーバを使用したい場合は 以下のように定義して使い回すこともできます。

type testMux struct{}
func (p *testMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {...}

また、CI にも乗せやすいので、外部依存のあるテストを書く際には重宝します。

事例として参考にしてもらえればと思います。