Go言語で、multipart/form-dataのテストをする

multipart/form-dataのテストを行いたい(featureテストのような)

ここのサイトがとても参考になりました
stackoverflow.com

HTML

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>{{ .Title }}</title>
  </head>
  <body>
    <form action="/file/upload_request" enctype="multipart/form-data" method="post">
      <input type="file" name="up_data" id="upload" multiple="multiple">
      <input type="submit" value="Upload file" />
    </form>
  </body>
</html>

このようなHTMLを使うとします。
up_dataがPOSTするファイル。

golangのソース

実際のコントローラはこのような感じ

func UploadFileRequest(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        w.WriteHeader(http.StatusBadRequest)
        return
    }

    file, fileHeader, err := r.FormFile("up_data")
    defer file.Close()
    if err != nil {
        w.WriteHeader(http.StatusBadRequest)
        fmt.Fprint(w, "file is needed")
        return
    }

    buf := bytes.NewBuffer(nil)
    if _, err := io.Copy(buf, file); err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        fmt.Fprint(w, "save file failed.")
        return
    }

    fmt.Fprintf(w, "file: %v", fileHeader)

    // ファイルを保存するような処理(usecaseを使ったり)
}

これのテストを行いたい

テストコード

func TestFileHTMLHandler(t *testing.T) {
    t.Run("UploadFile正常系", func(t *testing.T) {
        var b bytes.Buffer

        // multipartのリクエスト用オブジェクト
        w := multipart.NewWriter(&b)

        // ハンドラの準備(テスト用リクエスト)
        spy := &fakeFileUseCase{
            FakeCreate: func(files.File) (*files.File, error) {
                return nil, nil
            },
        }

        ts := httptest.NewServer(http.HandlerFunc(UploadFileRequest))
        defer ts.Close()
        client := ts.Client()

        var fw io.Writer
        f, err := os.Open("main.go")
        if err != nil {
            fmt.Errorf("err should be nil: %v", err)
        }

        w.CreateFormFile("up_data", "main.go")
        written, err := io.Copy(fw, f)
        if written == 0 {
            fmt.Errorf("written should be a positive value: %v", err)
        }

        w.Close()

        req, err := http.NewRequest("POST", ts.URL, &b)
        if err != nil {
            fmt.Errorf("err should be nil: %v", err)
        }
        // Don't forget to set the content type, this will contain the boundary.
        req.Header.Set("Content-Type", w.FormDataContentType())

        // Submit the request
        res, err := client.Do(req)
        if err != nil {
            fmt.Errorf("err should be nil: %v", err)
        }

        // Check the response
        if res.StatusCode != http.StatusOK {
            fmt.Errorf("bad status: %s", res.Status)
        }
    })
}

補足

  • ts := httptest.NewServerを使ってテスト用のサーバを準備
  • http.NewRequest("POST", ts.URL, &b)でPOSTのテスト(URLはhttptest.NewServerによって準備されたテキトーなURL)
  • w.CreateFormFile("up_data", "main.go") により、up_dataという名前でmain.goをPOSTするファイルとして準備