LoginSignup
32
32

More than 5 years have passed since last update.

Goで外部リクエストが関わる処理をテストする

Posted at

外部リクエストを含める処理の場合のテストの書き方
いわゆるリクエストのモックの作り方について。

例えばfetch()という外部のURLをGETで取得する関数があったとする。

app.go
package main

import (
  "fmt"
  "io/ioutil"
  "log"
  "net/http"
)

func fetch(url string) []byte {
  res, err := http.Get(url)
  fmt.Println(url)
  if err != nil {
    log.Fatal(err)
  }
  defer res.Body.Close()

  body, err := ioutil.ReadAll(res.Body)
  if err != nil {
    log.Fatal(err)
  }
  return body
}

func main() {
  clientId := "<client id>"
  body := fetch("https://api.instagram.com/v1/media/popular?client_id=" + clientId)
  fmt.Printf("%s", body)
}

ただ、外部サービスへのリクエストは送りたくない。
その場合httptestパッケージを使って、特定のURLの場合に返すheaderの値を設定し
そちらのURLにリクエストを送信するという簡易なモックサーバーを作成する。

この場合httptestパッケージというものを利用してwebサーバーのモックを用意して
そこにアクセスするようにする

使い方

func NewServer(handler http.Handler) *Server
使ってリクエストで返したいhandlerを登録する。

  handler := func(w http.ResponseWriter, r *http.Request) {
    // Check request.
    if g, w := r.URL.Path, request.path; g != w {
      t.Errorf("request got path %s, want %s", g, w)
    }

    // Send response.
    w.Header().Set("Content-Type", request.contenttype)
    io.WriteString(w, request.body)
  }

  server := httptest.NewServer(
     http.HandlerFunc(handler),
  )

app.goを使うと以下のような感じ。(json parseは適当)

app_test.go
package main

import (
  "encoding/json"
  "io"
  "net/http"
  "net/http/httptest"
  "testing"
)

type Response struct {
  path, query, contenttype, body string
}

func TestApp(t *testing.T) {

  response := &Response{
    path:        "/v1/media/popular",
    contenttype: "application/json",
    body: `{
      "meta": {
        "code": 200
      },
      "data": [{
        "attribution": null,
        "comments": {
          "count": 0,
          "data": []
        },
        "filter": "Normal",
        "created_time": "1407830189",
        "id": "785258998994306042_21773839"
      }]
    }`,
  }
  handler := func(w http.ResponseWriter, r *http.Request) {
    // Check request.
    if g, w := r.URL.Path, response.path; g != w {
      t.Errorf("request got path %s, want %s", g, w)
    }

    // Send response.
    w.Header().Set("Content-Type", response.contenttype)
    io.WriteString(w, response.body)
  }

  server := httptest.NewServer(http.HandlerFunc(handler))
  defer server.Close()

  res := fetch(server.URL + "/v1/media/popular")

  var media map[string]interface{}
  err := json.Unmarshal(res, &media)
  if err != nil {
    t.Fatal(err)
  }

  dataArr := media["data"].([]interface{})
  data := dataArr[0].(map[string]interface{})

  if data["id"] != "785258998994306042_21773839" {
    t.Fatal("Error")
  }
}

まとめ

  • httptestパッケージで任意のレスポンスを返すことが出来る
    • リクエストと、レスポンス結果の両方にテストが書けるようになる。
  • ただし、URLが固定されるのでリクエストは引数などで外から設定出来る必要がある
32
32
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
32