外部リクエストを含める処理の場合のテストの書き方
いわゆるリクエストのモックの作り方について。
例えば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が固定されるのでリクエストは引数などで外から設定出来る必要がある