GoでREST APIを呼ぶテストにhttp.FileServerを使う

例えばGET /users/:idのようなAPIの場合、

testdata
└── users
    └── 1

のように、testdata配下のusersディレクトリにidをファイル名としたJSONファイルを配置しておきます。

そうすると、

http.FileServer(http.Dir("testdata"))

で、testdata配下のパスとリクエストのパスが一致するファイルが

  • 存在すれば200 OKでそのファイルの中身
  • 存在しなければ404 Not Found

を返すサーバが作れます。

http.Get(os.Getenv("API_URL") + "/users/" + strconv.FormatInt(id, 10))

のような呼び出し方をしていればhttptestを使って次のようにテストが書けます。

package api

import (
    "net/http"
    "net/http/httptest"
    "os"
    "reflect"
    "testing"
)

func Test_getUser(t *testing.T) {
    ts := httptest.NewServer(http.FileServer(http.Dir("testdata")))
    defer ts.Close()

    if err := os.Setenv("API_URL", ts.URL); err != nil {
        t.Fatal(err)
    }
    defer os.Unsetenv("API_URL")

    type args struct {
        id int64
    }
    tests := []struct {
        name    string
        args    args
        want    *user
        wantErr bool
    }{
        {
            name:    "OK",
            args:    args{id: 1},
            want:    &user{ID: 1, Name: "Alice"},
            wantErr: false,
        },
        {
            name:    "NotFound",
            args:    args{id: 2},
            want:    nil,
            wantErr: true,
        },
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := getUser(tt.args.id)
            if (err != nil) != tt.wantErr {
                t.Errorf("getUser() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if !reflect.DeepEqual(got, tt.want) {
                t.Errorf("getUser() = %v, want %v", got, tt.want)
            }
        })
    }
}

POSTメソッドの場合、

  • エンドポイントがPOST /usersのような形式になるのと、
  • 201 Created500 Internal Server Errorが返せないため、

http.FileServerよりはhttp.HandlerFuncを使った方が良いでしょう。
また、リクエストパスとファイルが1対1にならない時も同様です。