> For the complete documentation index, see [llms.txt](https://andmorefine.gitbook.io/learn-go-with-tests/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://andmorefine.gitbook.io/learn-go-with-tests/build-an-application/json.md).

# JSON、ルーティング、埋め込み

JSON、ルーティング、埋め込み

[**この章のすべてのコードはここにあります**](https://github.com/quii/learn-go-with-tests/tree/main/json)

[前の章](/learn-go-with-tests/build-an-application/http-server.md)では、プレーヤーが勝ったゲームの数を保存するWebサーバーを作成しました。

プロダクトオーナーから新しい要件が届きました。保存されているすべてのプレーヤーのリストを返す`/league`という新しいエンドポイントがほしいとのことです。彼女はこれをJSONで返してほしいと考えています。

## ここまでのコード

```go
// server.go
package main

import (
	"fmt"
	"net/http"
	"strings"
)

type PlayerStore interface {
	GetPlayerScore(name string) int
	RecordWin(name string)
}

type PlayerServer struct {
	store PlayerStore
}

func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	player := strings.TrimPrefix(r.URL.Path, "/players/")

	switch r.Method {
	case http.MethodPost:
		p.processWin(w, player)
	case http.MethodGet:
		p.showScore(w, player)
	}
}

func (p *PlayerServer) showScore(w http.ResponseWriter, player string) {
	score := p.store.GetPlayerScore(player)

	if score == 0 {
		w.WriteHeader(http.StatusNotFound)
	}

	fmt.Fprint(w, score)
}

func (p *PlayerServer) processWin(w http.ResponseWriter, player string) {
	p.store.RecordWin(player)
	w.WriteHeader(http.StatusAccepted)
}
```

```go
// in_memory_player_store.go
package main

func NewInMemoryPlayerStore() *InMemoryPlayerStore {
	return &InMemoryPlayerStore{map[string]int{}}
}

type InMemoryPlayerStore struct {
	store map[string]int
}

func (i *InMemoryPlayerStore) RecordWin(name string) {
	i.store[name]++
}

func (i *InMemoryPlayerStore) GetPlayerScore(name string) int {
	return i.store[name]
}

```

```go
// main.go
package main

import (
	"log"
	"net/http"
)

func main() {
	server := &PlayerServer{NewInMemoryPlayerStore()}
	log.Fatal(http.ListenAndServe(":5000", server))
}
```

章の上部にあるリンクで対応するテストを見つけることができます。

まず、リーグ表のエンドポイントを作成します。

## 最初にテストを書く

いくつかの便利なテスト関数と偽の`PlayerStore`を使用するため、既存のスイートを拡張します。

```go
//server_test.go
func TestLeague(t *testing.T) {
	store := StubPlayerStore{}
	server := &PlayerServer{&store}

	t.Run("it returns 200 on /league", func(t *testing.T) {
		request, _ := http.NewRequest(http.MethodGet, "/league", nil)
		response := httptest.NewRecorder()

		server.ServeHTTP(response, request)

		assertStatus(t, response.Code, http.StatusOK)
	})
}
```

実際のスコアとJSONについて心配する前に、目標に向かって少しずつ反復していく方針で、変更を小さく保つようにします。最も簡単な出発点は、`/league`にアクセスして`OK`が返ってくることを確認することです。

## テストを実行してみます

```
    --- FAIL: TestLeague/it_returns_200_on_/league (0.00s)
        server_test.go:101: status code is wrong: got 404, want 200
FAIL
FAIL	playerstore	0.221s
FAIL
```

私たちの `PlayerServer` は `404 Not Found` を返し、まるで未知のプレーヤーの勝利数を取得しようとしたかのようです。`server.go` がどのように `ServeHTTP` を実装しているかを見てみると、常に特定のプレーヤーを指す URL で呼び出されることを想定していることが分かります。

```go
player := strings.TrimPrefix(r.URL.Path, "/players/")
```

前章で、これはかなり素朴なルーティングの方法であると述べました。このテストは、異なるリクエストパスをどのように扱うかという概念が必要であることを正しく知らせてくれています。

## 成功させるのに十分なコードを書く

Goには[`ServeMux`](https://golang.org/pkg/net/http/#ServeMux) (request multiplexer)と呼ばれるルーティング機構が組み込まれており、`http.Handler`を特定のリクエストパスにアタッチすることができます。

テストが通りさえすれば安全にリファクタリングできるとわかっているので、ここは多少の罪を犯してでも、最速でテストを通しましょう。

```go
//server.go
func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	router := http.NewServeMux()

	router.Handle("/league", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	}))

	router.Handle("/players/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		player := strings.TrimPrefix(r.URL.Path, "/players/")

		switch r.Method {
		case http.MethodPost:
			p.processWin(w, player)
		case http.MethodGet:
			p.showScore(w, player)
		}
	}))

	router.ServeHTTP(w, r)
}
```

* リクエストの開始時にはルータを作成し、`x`のパスには`y`ハンドラーを使用するように指示します。
* 新しいエンドポイントには`http.HandlerFunc`を使用し、`/league`がリクエストされたときに `w.WriteHeader(http.StatusOK)` を実行する *無名関数* を使用して、新しいテストをパスするようにします。
* `/players/`のルートについては、コードを切り取り、別の`http.HandlerFunc`に貼り付けています。
* 最後に、新しいルータの `ServeHTTP` を呼び出して、受け取ったリクエストを処理します（`ServeMux`も *また* `http.Handler`であることに気づきましたか？）。

これでテストはパスするはずです。

## リファクタリング♪

`ServeHTTP`はかなり大きく見えます。ハンドラーを別のメソッドにリファクタリングすることで、物事を少し分離することができます。

```go
//server.go
func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	router := http.NewServeMux()
	router.Handle("/league", http.HandlerFunc(p.leagueHandler))
	router.Handle("/players/", http.HandlerFunc(p.playersHandler))

	router.ServeHTTP(w, r)
}

func (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
}

func (p *PlayerServer) playersHandler(w http.ResponseWriter, r *http.Request) {
	player := strings.TrimPrefix(r.URL.Path, "/players/")

	switch r.Method {
	case http.MethodPost:
		p.processWin(w, player)
	case http.MethodGet:
		p.showScore(w, player)
	}
}
```

リクエストが来るたびにルータを設定してから呼び出すというのは、かなり奇妙です（それに非効率です）。理想的には、依存関係を受け取り、ルータを作成する一度きりのセットアップを行う`NewPlayerServer`のような関数を用意したいところです。そうすれば、各リクエストはそのルータのインスタンスを1つ使うだけで済みます。

```go
//server.go
type PlayerServer struct {
	store  PlayerStore
	router *http.ServeMux
}

func NewPlayerServer(store PlayerStore) *PlayerServer {
	p := &PlayerServer{
		store,
		http.NewServeMux(),
	}

	p.router.Handle("/league", http.HandlerFunc(p.leagueHandler))
	p.router.Handle("/players/", http.HandlerFunc(p.playersHandler))

	return p
}

func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p.router.ServeHTTP(w, r)
}
```

* `PlayerServer`はルータを保存する必要があります。
* ルーティングの作成を`ServeHTTP`から`NewPlayerServer`に移動したので、これはリクエストごとではなく一度だけで済みます。
* これまで`PlayerServer{&store}`としていたテストコードと本番コードをすべて、`NewPlayerServer(&store)`を使うように更新する必要があります。

### 最後のリファクタリング

以下のようにコードを変更してみてください。

```go
type PlayerServer struct {
	store PlayerStore
	http.Handler
}

func NewPlayerServer(store PlayerStore) *PlayerServer {
	p := new(PlayerServer)

	p.store = store

	router := http.NewServeMux()
	router.Handle("/league", http.HandlerFunc(p.leagueHandler))
	router.Handle("/players/", http.HandlerFunc(p.playersHandler))

	p.Handler = router

	return p
}
```

そして、`server_test.go`, `server_integration_test.go`, `main.go` 内の `server := &PlayerServer{&store}` を `server := NewPlayerServer(&store)` に置き換えてみてください。

最後に、`func (p *PlayerServer) ServeHTTP(w http.ResponseWriter, r *http.Request)`が不要になったので、**削除**してください。

## 埋め込み

`PlayerServer`の2番目のプロパティを変更し、名前付きプロパティ`router http.ServeMux`を削除して、`http.Handler`に置き換えました。 これは *埋め込み（embedding）* と呼ばれます。

> Go は典型的な型駆動型のサブクラス化の概念を提供しませんが、構造体やインターフェース内に型を埋め込むことで、実装の一部を「借用」する機能があります。

[Effective Go - Embedding](https://golang.org/doc/effective_go.html#embedding)

これが意味するのは、私たちの`PlayerServer`が`http.Handler`の持つすべてのメソッドを持つようになったということです。といっても、それは`ServeHTTP`だけですが。

この`http.Handler`を「埋める」ために、`NewPlayerServer`で作成した`router`に割り当てます。これができるのは、`http.ServeMux`が`ServeHTTP`というメソッドを持っているからです。

埋め込み型を介してすでに公開しているため、これにより独自の`ServeHTTP`メソッドを削除できます。

埋め込みは非常に興味深い言語機能です。インターフェースと一緒に使うことで、新しいインターフェースを構成できます。

```go
type Animal interface {
	Eater
	Sleeper
}
```

また、インターフェースだけでなく、具象型でも使用できます。予想どおり、具象型を埋め込むと、そのすべてのパブリックメソッドとフィールドにアクセスできるようになります。

### 欠点はありますか？

埋め込んだ型のすべてのパブリックメソッドとフィールドが公開されてしまうため、型を埋め込む際には注意が必要です。私たちの場合は、公開したい *インターフェース* （`http.Handler`）だけを埋め込んだので問題ありません。

もし横着して、代わりに`http.ServeMux`（具象型）を埋め込んでいたとしても動作はします *が* 、`Handle(path, handler)`が公開されてしまうため、`PlayerServer`のユーザーが私たちのサーバーに新しいルートを追加できてしまいます。

**型を埋め込むときは、公開APIにどのような影響があるかをよく考えてください**

埋め込みを誤用してAPIを汚染し、型の内部を公開してしまうのは、 *非常に* よくある間違いです。

アプリケーションを再構成したことで、新しいルートを簡単に追加できるようになり、`/league`エンドポイントの土台もできました。次は、何か有用な情報を返すようにする必要があります。

このようなJSONを返す必要があります。

```json
[
   {
      "Name":"Bill",
      "Wins":10
   },
   {
      "Name":"Alice",
      "Wins":15
   }
]
```

## 最初にテストを書く

まず、応答を意味のあるものに解析することから始めます。

```go
//server_test.go
func TestLeague(t *testing.T) {
	store := StubPlayerStore{}
	server := NewPlayerServer(&store)

	t.Run("it returns 200 on /league", func(t *testing.T) {
		request, _ := http.NewRequest(http.MethodGet, "/league", nil)
		response := httptest.NewRecorder()

		server.ServeHTTP(response, request)

		var got []Player

		err := json.NewDecoder(response.Body).Decode(&got)

		if err != nil {
			t.Fatalf("Unable to parse response from server %q into slice of Player, '%v'", response.Body, err)
		}

		assertStatus(t, response.Code, http.StatusOK)
	})
}
```

### JSON文字列をテストしないのはなぜですか？

最初のステップとしては、応答の本文が特定のJSON文字列であることをアサートするだけのほうが単純だ、という意見もあるでしょう。

私の経験では、JSON文字列に対して評価するテストには次の問題があります。

* もろさ。データモデルを変更すると、テストは失敗します。
* デバッグが難しい。 2つのJSON文字列を比較するときに実際の問題が何であるかを理解するのは難しい場合があります。
* 意図が伝わりにくい。出力はJSONである必要がありますが、本当に重要なのは、データがエンコードされる方法ではなく、データが正確に何であるかです。
* 標準ライブラリの再テスト。標準ライブラリがJSONを出力する方法をテストする必要はありません。すでにテストされています。他の人のコードをテストしないでください。

代わりに、JSONを解析して、テストに関連するデータ構造に変換する必要があります。

### データモデリング

JSONデータモデルを考えると、いくつかのフィールドを持つ`Player`の配列が必要なようですので、これを表す新しい型を作成しました。

```go
//server.go
type Player struct {
	Name string
	Wins int
}
```

### JSONデコード

```go
//server_test.go
var got []Player
err := json.NewDecoder(response.Body).Decode(&got)
```

JSONを解析してデータモデルにするには、 `encoding/json`パッケージから`Decoder`を作成し、その`Decode`メソッドを呼び出します。`Decoder`を作成するには、読み取るための`io.Reader`が必要です。この場合は、応答スパイの`Body`です。

`Decode`は、デコードしようとしているもののアドレスを受け取ります。そのため、前の行で`Player`の空のスライスを宣言します。

JSONの解析が失敗する可能性があるため、`Decode`が`error`を返す可能性があります。それが失敗した場合にテストを続行する意味はないので、エラーを確認し、エラーが発生した場合は `t.Fatalf`でテストを停止します。テストを実行している誰かが解析できない文字列を確認することが重要であるため、エラーとともに応答本文を表示することに注意してください。

## テストを実行してみます

```
=== RUN   TestLeague/it_returns_200_on_/league
    --- FAIL: TestLeague/it_returns_200_on_/league (0.00s)
        server_test.go:107: Unable to parse response from server '' into slice of Player, 'unexpected end of JSON input'
```

現在、エンドポイントは本文を返さないため、JSONに解析できません。

## 成功させるのに十分なコードを書く

```go
//server.go
func (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {
	leagueTable := []Player{
		{"Chris", 20},
	}

	json.NewEncoder(w).Encode(leagueTable)

	w.WriteHeader(http.StatusOK)
}
```

テストに成功しました。

### エンコードとデコード

標準ライブラリの素敵な対称性に注目してください。

* `Encoder`を作成するには、`http.ResponseWriter`が実装する`io.Writer`が必要です。
* `Decoder`を作成するには、レスポンススパイの`Body`フィールドが実装する`io.Reader`が必要です。

本書ではこれまで`io.Writer`を使ってきましたが、これは`io.Writer`が標準ライブラリでいかに広く使われているか、そして多くのライブラリがいかに簡単にそれと連携できるかを示すもう1つの例です。

## リファクタリング♪

`leagueTable`はもうすぐハードコードしなくなることがわかっているので、ハンドラーと`leagueTable`の取得との間で関心の分離を導入しておくとよいでしょう。

```go
//server.go
func (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {
	json.NewEncoder(w).Encode(p.getLeagueTable())
	w.WriteHeader(http.StatusOK)
}

func (p *PlayerServer) getLeagueTable() []Player {
	return []Player{
		{"Chris", 20},
	}
}
```

次に、テストを拡張して、必要なデータを正確に制御できるようにします。

## 最初にテストを書く

テストを更新して、ストアにスタブとして用意したプレーヤーがリーグ表に含まれていることをアサートできます。

リーグを保存できるように`StubPlayerStore`を更新します。リーグは`Player`のスライスにすぎません。期待するデータをそこに保存します。

```go
//server_test.go
type StubPlayerStore struct {
	scores   map[string]int
	winCalls []string
	league   []Player
}
```

次に、スタブの`league`プロパティにプレーヤーを何人か入れて現在のテストを更新し、それらがサーバーから返されることをアサートします。

```go
//server_test.go
func TestLeague(t *testing.T) {

	t.Run("it returns the league table as JSON", func(t *testing.T) {
		wantedLeague := []Player{
			{"Cleo", 32},
			{"Chris", 20},
			{"Tiest", 14},
		}

		store := StubPlayerStore{nil, nil, wantedLeague}
		server := NewPlayerServer(&store)

		request, _ := http.NewRequest(http.MethodGet, "/league", nil)
		response := httptest.NewRecorder()

		server.ServeHTTP(response, request)

		var got []Player

		err := json.NewDecoder(response.Body).Decode(&got)

		if err != nil {
			t.Fatalf("Unable to parse response from server %q into slice of Player, '%v'", response.Body, err)
		}

		assertStatus(t, response.Code, http.StatusOK)

		if !reflect.DeepEqual(got, wantedLeague) {
			t.Errorf("got %v want %v", got, wantedLeague)
		}
	})
}
```

## テストを実行してみます

```
./server_test.go:33:3: too few values in struct initializer
./server_test.go:70:3: too few values in struct initializer
```

## テストを実行するための最小限のコードを記述し、失敗したテスト出力を確認します

`StubPlayerStore`に新しいフィールドがあるので、他のテストを更新する必要があります。他のテストでは`nil`に設定します。

テストをもう一度実行すると、次のようになるはずです。

```
=== RUN   TestLeague/it_returns_the_league_table_as_JSON
    --- FAIL: TestLeague/it_returns_the_league_table_as_JSON (0.00s)
        server_test.go:124: got [{Chris 20}] want [{Cleo 32} {Chris 20} {Tiest 14}]
```

## 成功させるのに十分なコードを書く

データが`StubPlayerStore`にあること、そしてそれをインターフェース`PlayerStore`として抽象化していることはわかっています。`PlayerStore`を渡してくる人が誰でもリーグのデータを提供できるように、これを更新する必要があります。

```go
//server.go
type PlayerStore interface {
	GetPlayerScore(name string) int
	RecordWin(name string)
	GetLeague() []Player
}
```

これで、ハードコードされたリストを返すのではなく、ハンドラーコードを更新してそれを呼び出すことができます。メソッド`getLeagueTable()`を削除してから、`leagueHandler`を更新して`GetLeague()`を呼び出します。

```go
//server.go
func (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {
	json.NewEncoder(w).Encode(p.store.GetLeague())
	w.WriteHeader(http.StatusOK)
}
```

テストを実行してみてください。

```
# github.com/quii/learn-go-with-tests/json-and-io/v4
./main.go:9:50: cannot use NewInMemoryPlayerStore() (type *InMemoryPlayerStore) as type PlayerStore in argument to NewPlayerServer:
    *InMemoryPlayerStore does not implement PlayerStore (missing GetLeague method)
./server_integration_test.go:11:27: cannot use store (type *InMemoryPlayerStore) as type PlayerStore in argument to NewPlayerServer:
    *InMemoryPlayerStore does not implement PlayerStore (missing GetLeague method)
./server_test.go:36:28: cannot use &store (type *StubPlayerStore) as type PlayerStore in argument to NewPlayerServer:
    *StubPlayerStore does not implement PlayerStore (missing GetLeague method)
./server_test.go:74:28: cannot use &store (type *StubPlayerStore) as type PlayerStore in argument to NewPlayerServer:
    *StubPlayerStore does not implement PlayerStore (missing GetLeague method)
./server_test.go:106:29: cannot use &store (type *StubPlayerStore) as type PlayerStore in argument to NewPlayerServer:
    *StubPlayerStore does not implement PlayerStore (missing GetLeague method)
```

`InMemoryPlayerStore`と`StubPlayerStore`には、インターフェースに追加した新しいメソッドがないため、コンパイラが文句を言っています。

`StubPlayerStore`の場合は非常に簡単です。先ほど追加した`league`フィールドを返すだけです。

```go
//server_test.go
func (s *StubPlayerStore) GetLeague() []Player {
	return s.league
}
```

`InMemoryStore`の実装がどうなっていたか、おさらいしておきましょう。

```go
//in_memory_player_store.go
type InMemoryPlayerStore struct {
	store map[string]int
}
```

マップを反復処理すれば`GetLeague`を「ちゃんと」実装するのはかなり簡単ですが、私たちは *テストをパスさせるための最小限のコードを書こうとしている* ことを思い出してください。

そこで今のところはコンパイラを満足させるだけにして、`InMemoryStore`の実装が不完全だという居心地の悪さには目をつぶりましょう。

```go
//in_memory_player_store.go
func (i *InMemoryPlayerStore) GetLeague() []Player {
	return nil
}
```

これが本当に意味しているのは、 *後で* これをテストしたくなるということですが、ひとまず保留にしておきましょう。

テストを実行してみてください。コンパイルが通り、テストも成功するはずです！

## リファクタリング♪

テストコードは意図をうまく伝えておらず、リファクタリングできる定型文がたくさんあります。

```go
//server_test.go
t.Run("it returns the league table as JSON", func(t *testing.T) {
	wantedLeague := []Player{
		{"Cleo", 32},
		{"Chris", 20},
		{"Tiest", 14},
	}

	store := StubPlayerStore{nil, nil, wantedLeague}
	server := NewPlayerServer(&store)

	request := newLeagueRequest()
	response := httptest.NewRecorder()

	server.ServeHTTP(response, request)

	got := getLeagueFromResponse(t, response.Body)
	assertStatus(t, response.Code, http.StatusOK)
	assertLeague(t, got, wantedLeague)
})
```

ここに新しいヘルパーがあります

```go
//server_test.go
func getLeagueFromResponse(t testing.TB, body io.Reader) (league []Player) {
	t.Helper()
	err := json.NewDecoder(body).Decode(&league)

	if err != nil {
		t.Fatalf("Unable to parse response from server %q into slice of Player, '%v'", body, err)
	}

	return
}

func assertLeague(t testing.TB, got, want []Player) {
	t.Helper()
	if !reflect.DeepEqual(got, want) {
		t.Errorf("got %v want %v", got, want)
	}
}

func newLeagueRequest() *http.Request {
	req, _ := http.NewRequest(http.MethodGet, "/league", nil)
	return req
}
```

サーバーが機能するために必要な最後の1つは、`JSON`を返していることをマシンが認識できるように、応答で`content-type`ヘッダーを返すことを確認することです。

## 最初にテストを書く

このアサーションを既存のテストに追加します

```go
//server_test.go
if response.Result().Header.Get("content-type") != "application/json" {
	t.Errorf("response did not have content-type of application/json, got %v", response.Result().Header)
}
```

## テストを実行してみます

```
=== RUN   TestLeague/it_returns_the_league_table_as_JSON
    --- FAIL: TestLeague/it_returns_the_league_table_as_JSON (0.00s)
        server_test.go:124: response did not have content-type of application/json, got map[Content-Type:[text/plain; charset=utf-8]]
```

## 成功させるのに十分なコードを書く

`leagueHandler`を更新します

```go
//server.go
func (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("content-type", "application/json")
	json.NewEncoder(w).Encode(p.store.GetLeague())
}
```

テストは成功するはずです。

## リファクタリング♪

`application/json` の定数を作成し、それを `leagueHandler` で使用します。

```go
//server.go
const jsonContentType = "application/json"

func (p *PlayerServer) leagueHandler(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("content-type", jsonContentType)
	json.NewEncoder(w).Encode(p.store.GetLeague())
}
```

それから `assertContentType`のヘルパーを追加します

```go
//server_test.go
func assertContentType(t testing.TB, response *httptest.ResponseRecorder, want string) {
	t.Helper()
	if response.Result().Header.Get("content-type") != want {
		t.Errorf("response did not have content-type of %s, got %v", want, response.Result().Header)
	}
}
```

テストで使用してください。

```go
//server_test.go
assertContentType(t, response, jsonContentType)
```

`PlayerServer`はひとまず片付いたので、`InMemoryPlayerStore`に目を向けましょう。今のままプロダクトオーナーにデモしようとしても、`/league`は動かないからです。

確信を得るための最も簡単な方法は、統合テストに追加することです。新しいエンドポイントにアクセスして、 `/league`から正しい応答が返されることを確認できます。

## 最初にテストを書く

`t.Run`を使用してこのテストを少し分解し、サーバーテストのヘルパーを再利用できます。これもテストをリファクタリングすることの重要性を示しています。

```go
//server_integration_test.go
func TestRecordingWinsAndRetrievingThem(t *testing.T) {
	store := NewInMemoryPlayerStore()
	server := NewPlayerServer(store)
	player := "Pepper"

	server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))
	server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))
	server.ServeHTTP(httptest.NewRecorder(), newPostWinRequest(player))

	t.Run("get score", func(t *testing.T) {
		response := httptest.NewRecorder()
		server.ServeHTTP(response, newGetScoreRequest(player))
		assertStatus(t, response.Code, http.StatusOK)

		assertResponseBody(t, response.Body.String(), "3")
	})

	t.Run("get league", func(t *testing.T) {
		response := httptest.NewRecorder()
		server.ServeHTTP(response, newLeagueRequest())
		assertStatus(t, response.Code, http.StatusOK)

		got := getLeagueFromResponse(t, response.Body)
		want := []Player{
			{"Pepper", 3},
		}
		assertLeague(t, got, want)
	})
}
```

## テストを実行してみます

```
=== RUN   TestRecordingWinsAndRetrievingThem/get_league
    --- FAIL: TestRecordingWinsAndRetrievingThem/get_league (0.00s)
        server_integration_test.go:35: got [] want [{Pepper 3}]
```

## 成功させるのに十分なコードを書く

`GetLeague()`を呼び出すと、`InMemoryPlayerStore`は`nil`を返すため、修正する必要があります。

```go
//in_memory_player_store.go
func (i *InMemoryPlayerStore) GetLeague() []Player {
	var league []Player
	for name, wins := range i.store {
		league = append(league, Player{name, wins})
	}
	return league
}
```

必要なのは、マップを反復処理して、各キー/値を`Player`に変換することだけです。

これでテストに成功するはずです。

## まとめ

TDDを使用してプログラムを安全に反復し続け、ルーターで保守可能な方法で新しいエンドポイントをサポートし、コンシューマーにJSONを返すことができるようになりました。次の章では、データの永続化とリーグの並べ替えについて説明します。

ここで習得したもの

* **ルーティング**. 標準ライブラリには、ルーティングを行うための使いやすい型が用意されています。標準ライブラリは`http.Handler`インターフェースを完全に取り入れており、`Handler`にルートを割り当て、ルータ自身も`Handler`になります。ただし、パス変数（例: `/users/{id}`）のような、期待されそうな機能のいくつかはありません。この情報を自分で簡単に解析することはできますが、それが面倒になったら他のルーティングライブラリを見ることを検討したほうがいいかもしれません。人気のあるもののほとんどは、`http.Handler`も実装するという標準ライブラリの哲学に従っています。
* **型の埋め込み**. この手法については少し触れただけですが、[Effective Goでさらに詳しく学べます](https://golang.org/doc/effective_go.html#embedding)。ここから1つだけ持ち帰るとしたら、埋め込みは非常に便利ですが、 *常にパブリックAPIのことを考え、適切なものだけを公開する* ということです。
* **JSONのデシリアライズとシリアライズ**. 標準ライブラリは、データのシリアライズとデシリアライズを非常に簡単にしてくれます。また、設定も可能で、必要に応じてこれらのデータ変換がどのように動作するかをカスタマイズできます。
