Goで「結合先のWebサーバ」を簡単にモック化する【gock】

# はじめに
結合試験を自動化するために対向システムをモック化することはよくあると思います。
Goでは`gock`と呼ばれる外部パッケージを使って簡単にHTTPのモックサーバを作ることができます。
gockを紹介する日本語記事が見つからなかったので、[公式のREADME](https://github.com/h2non/gock/blob/master/README.md)をもとにまとめてみました。

他のGoのモック系は別の記事にまとめています。
[Goでメソッドを簡単にモック化する【gomock】](https://qiita.com/gold-kou/items/81562f9142323b364a60)
[Goでデータベースを簡単にモック化する【sqlmock】](https://qiita.com/gold-kou/items/cb174690397f651e2d7f)

# インストール
gockは下記でインストールできます。

```console
$ go get -u gopkg.in/h2non/gock.v1
```

また、公式で扱われている具体例では`github.com/nbio/st`(実際値と期待値の比較を行う外部パッケージ)も利用しているので、それも使う場合は併せてインストールします。

```console
$ go get -u github.com/nbio/st
```

もちろん、`nbio/st`を使わずにgockを利用することは可能です。（現に、自分の担当プロジェクトでは代わりにtestifyを使っています。）

# モックサーバの作り方
`gock.New()`にモックサーバの設定をしていきます。

```go
func TestFoo(t *testing.T) {
  // 関数終了後に自動でモックサーバを停止させる。お決まりの書き方。
  defer gock.Off()

  // モックサーバの設定と起動
  gock.New("http://server.com"). // ドメイン名を設定
					Get("/bar").                          // HTTPメソッド種類とAPIパスを設定
					Reply(200).                           // HTTPステータスコードを設定
					JSON(map[string]string{"foo": "bar"}) // HTTPレスポンスボディを設定

  // テスト内容を下記に書いていきます
}
```

上記のコードにより、`http://server.com/bar`にGETメソッドでリクエストがきたらステータスコード200で`{"foo": "bar"}`が返すモックサーバのできあがりです。

# 具体例
実際のテストの書き方を簡単な例で書いていきます。

## シンプルな例
最も単純な例です。

```go
func TestSimple(t *testing.T) {
	defer gock.Off()

	gock.New("http://foo.com").
		Get("/bar").
		Reply(200).
		JSON(map[string]string{"foo": "bar"})

	// モックサーバへリクエスト
	res, err := http.Get("http://foo.com/bar")

	// stを使って期待通りかを確認
	st.Expect(t, err, nil)
	st.Expect(t, res.StatusCode, 200)
	body, _ := ioutil.ReadAll(res.Body)
	st.Expect(t, string(body)[:13], `{"foo":"bar"}`)

	// pendingになっているモックサーバが無いことを確認。お決まりの書き方。
	st.Expect(t, gock.IsDone(), true)
}
```

## ヘッダを使った例
モックサーバにヘッダを設定することも可能です。

```go
func TestMatchHeaders(t *testing.T) {
	defer gock.Off()

	gock.New("http://foo.com").
		MatchHeader("Authorization", "^foo bar$"). // Authorizationをヘッダに設定
		MatchHeader("API", "1.[0-9]+").            // APIバージョンをヘッダに設定
		HeaderPresent("Accept").                   // メディアタイプをヘッダに指定。なんでもOKとする。
		Reply(200).
		BodyString("foo foo")

	req, err := http.NewRequest("GET", "http://foo.com", nil)
	// モックサーバが要求するヘッダの値を設定
	req.Header.Set("Authorization", "foo bar")
	req.Header.Set("API", "1.0")
	req.Header.Set("Accept", "text/plain")

	res, err := (&http.Client{}).Do(req)
	st.Expect(t, err, nil)
	st.Expect(t, res.StatusCode, 200)
	body, _ := ioutil.ReadAll(res.Body)
	st.Expect(t, string(body), "foo foo")

	st.Expect(t, gock.IsDone(), true)
}
```

また、例のようにモックサーバのレスポンスボディはjsonだけでなくstringで返すことも可能です。

```
BodyString("foo foo")
```

他にXMLも用意されています。

## リクエストボディがJSONの例
リクエストボディをjsonで要求するモックサーバの例です。

```go
func TestMockSimple(t *testing.T) {
  defer gock.Off()

  gock.New("http://foo.com").
    Post("/bar").
    MatchType("json"). // リクエストボディはjsonとする
    JSON(map[string]string{"foo": "bar"}).
    Reply(201).
    JSON(map[string]string{"bar": "foo"})

  body := bytes.NewBuffer([]byte(`{"foo":"bar"}`))
  res, err := http.Post("http://foo.com/bar", "application/json", body)
  st.Expect(t, err, nil)
  st.Expect(t, res.StatusCode, 201)

  resBody, _ := ioutil.ReadAll(res.Body)
  st.Expect(t, string(resBody)[:13], `{"bar":"foo"}`)

  st.Expect(t, gock.IsDone(), true)
}
```

# 最後に
gockを使えば簡単にWebのモックサーバを作れることをご紹介しました。
メソッド仕様を細かく見る場合は[GoDoc](https://godoc.org/github.com/h2non/gock)を参照ください。
より多くの具体例は[こちら](https://github.com/h2non/gock/tree/master/_examples)に載っています。