/league
」という新しいエンドポイントを作成します。彼女はこれがJSONとして返されることを望んでいます。/league
」テーブルのエンドポイントを作成します。PlayerStore
を使用するため、既存のスイートを拡張します。/league
を押してOK
が返されることを確認することです。PlayerServer
は 404 Not Found
を返し、まるで未知のプレイヤーの勝利を得ようとしているかのようです。server.go
がどのように ServeHTTP
を実装しているかを見てみると、常に特定のプレイヤーを指す URL で呼び出されることを想定していることが分かります。x
のパスにはy
ハンドラを使用するように指示します。http.HandlerFunc
を使用し、/league
がリクエストされたときに w.WriteHeader(http.StatusOK)
を指定する anonymous function を使用して、新しいテストをパスするようにします。/players/
のルートについては、コードを切り取り、別のhttp.HandlerFunc
に貼り付けています。ServeHTTP
を呼び出して、リクエストを処理します。ServeHTTP
はかなり大きく見えます。ハンドラを別のメソッドにリファクタリングすることで、物事を少し分離することができます。NewPlayerServer
関数を持っていて、依存関係を取り込んで、 ルータを作成するための一度きりのセットアップを行うことです。それぞれのリクエストはそのルーターのインスタンスを使うだけです。PlayerServer
はルータを保存する必要があります。ServeHTTP
からNewPlayerServer
に移動したので、これはリクエストごとではなく一度だけで済みます。NewPlayerServer(&store)
で PlayerServer{&store}
を実行するために使用していたすべてのテストおよび製品コードを更新する必要があります。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)
が不要になったので、delete が不要になったので、削除してください。PlayerServer
の2番目のプロパティを変更し、名前付きプロパティrouter http.ServeMux
を削除して、http.Handler
に置き換えました。 これは 埋め込み(embedding) と呼ばれます。Go は典型的な型駆動型のサブクラス化の概念を提供しませんが、構造体やインターフェイス内に型を埋め込むことで、実装の一部を「借用」する機能があります。
PlayerServer
が http.Handler
が持つすべてのメソッドを持っているということです。これは単なる ServeHTTP
です。http.Handler
を「埋める」ために、NewPlayerServer
で作成したrouter
に割り当てます。これはhttp.ServeMux
がServeHTTP
というメソッドを持っているからです。ServeHTTP
メソッドを削除できます。http.Handler
)を公開したい interface だけを埋め込んだので問題ありません。http.ServeMux
ではなく具象型でも機能しますが、Handle(path、handler)
が原因で、PlayerServer
のユーザーはサーバーに新しいルートを追加できます公開する。/league
エンドポイントの開始を設定できます。ここで、いくつかの有用な情報を返すようにする必要があります。Player
の配列が必要なようですので、これをキャプチャする新しいタイプを作成しました。encoding/json
パッケージからDecoder
を作成し、そのDecode
メソッドを呼び出します。Decoder
を作成するには、読み取るためのio.Reader
が必要です。この場合は、応答スパイのBody
です。Decode
は、デコードしようとしているもののアドレスを受け取ります。そのため、前の行でPlayer
の空のスライスを宣言します。Decode
がerror
を返す可能性があります。それが失敗した場合にテストを続行する意味はないので、エラーを確認し、エラーが発生した場合は t.Fatalf
でテストを停止します。テストを実行している誰かが解析できない文字列を確認することが重要であるため、エラーとともに応答本文を表示することに注意してください。Encoder
を作成するには、http.ResponseWriter
が実装するio.Writer
が必要です。Decoder
を作成するには、レスポンススパイのBody
フィールドが実装するio.Reader
が必要です。io.Writer
を使用しました。これは、標準ライブラリでの普及と、多くのライブラリが簡単に動作することを示すもう1つのデモです。leagueTable
を取得することの間に懸念の分離を導入すると、すぐにはハードコードしないことになるので便利です。League
テーブルにストアでスタブするプレーヤーが含まれていることをアサートできます。League
を保存できるように、StubPlayerStore
を更新します。これは、Player
のスライスにすぎません。そこに期待されるデータを保存します。League
プロパティに一部のプレーヤーを配置して現在のテストを更新し、サーバーから返されることを評価します。StubPlayerStore
に新しいフィールドがあるので、他のテストを更新する必要があります。他のテストではnil
に設定します。StubPlayerStore
にあることを知っており、それをインターフェイス PlayerStore
に抽象化しました。PlayerStore
で私たちを渡す誰もがリーグのデータを提供できるように、これを更新する必要があります。getLeagueTable()
を削除してから、leagueHandler
を更新してGetLeague()
を呼び出します。InMemoryPlayerStore
とStubPlayerStore
には、インターフェイスに追加した新しいメソッドがないため、コンパイラーは不平を言っています。StubPlayerStore
の場合は非常に簡単です。先ほど追加したleague
フィールドを返すだけです。InMemoryStore
の実装方法について説明します。GetLeague
を「適切に」実装することは、マップを反復することでかなり簡単ですが、テストをパスするための最小限のコードを記述しようとしていることを思い出してください。InMemoryStore
での実装が不完全であるという不快な気持ちに耐えてみましょう。JSON
を返していることをマシンが認識できるように、応答でcontent-type
ヘッダーを返すことを確認することです。leagueHandler
を更新しますapplication/json
の定数を作成し、それを leagueHandler
で使用します。assertContentType
のヘルパーを追加しますPlayerServer
を整理したので、InMemoryPlayerStore
に目を向けることができます。これを製品の所有者にデモしようとした場合、/league
は機能しないためです。/league
から正しい応答が返されることを確認できます。t.Run
を使用してこのテストを少し分解し、サーバーテストのヘルパーを再利用できます。これもリファクタリングテストの重要性を示しています。GetLeague()
を呼び出すと、InMemoryPlayerStore
はnil
を返すため、修正する必要があります。Player
に変換することだけです。http.Handler
インターフェースを完全に取り入れており、Handler
にルートを割り当て、ルータ自身もHandler
になります。しかし、パス変数のような、あなたが期待するような機能はありません。この情報を自分で簡単に解析することはできますが、それが面倒になったら他のルーティングライブラリを見ることを検討したほうがいいかもしれません。人気のあるもののほとんどは、http.Handler
も実装するという標準ライブラリの哲学に固執しています。