GET /players/{name}
は、勝利の合計数を示す数値を返す必要がありますPOST /players/{name}
は、その名前の勝利を記録し、後続のPOST
ごとに増分する必要がありますテストを迅速に実行し、プロセスで必要なあらゆる罪を犯します。
GET
することはできず、GET
エンドポイントがすでに存在しない状態でPOST
が機能したかどうかを知るのは難しいようです。GET
にPlayerStore
thing が必要です。これはインターフェースである必要があるので、テストするときに、実際のストレージコードを実装する必要なく、コードをテストするための簡単なスタブを作成できます。POST
の場合、PlayerStore
への呼び出しを spy して、プレーヤーが正しく保存されていることを確認できます。保存の実装は検索と連動しません。Faking it
)」と呼んでいます。動作するテストができたら、その定数を削除するのに役立つテストをさらに記述できます。ServeHTTP
メソッドを実装することにより、ハンドラーインターフェースを実装します。1つ目は、レスポンスを書き込む場所で、2つ目はサーバーに送信されたHTTPリクエストです。server_test.go
というファイルを作成し、これらの2つの引数を受け取る関数PlayerServer
のテストを書いてみましょう。送信されるリクエストは、プレーヤーのスコアを取得することです。これは"20"
であると予想されます。Request
が必要であり、ハンドラーがResponseWriter
に書き込む内容を spy する必要があります。http.NewRequest
を使用してリクエストを作成します。最初の引数はリクエストのメソッドで、2番目はリクエストのパスです。nil
引数はリクエストの本文を参照します。この場合、設定する必要はありません。net/http/httptest
には、ResponseRecorder
というスパイが既に作成されているので、それを使用できます。応答として書き込まれた内容を検査するための多くの便利な方法があります。./server_test.go:13:2: undefined: PlayerServer
server.go
というファイルを作成し、PlayerServer
を定義しますGreet
関数を使用してHTTPサーバーに触れました。 net/httpのResponseWriter
もioWriter
を実装しているため、fmt.Fprint
を使用して文字列をHTTP応答として送信できることがわかりました。main.go
を作成し、このコードを配置します。.go
ファイルを取得してプログラムをビルドするgo build
を実行します。その後、./myprogram
で実行できます。http.HandlerFunc
Handler
インターフェースがサーバーを作るために実装する必要があるものであることを探りました。 通常は、struct
を作成してそれを行い、独自のServeHTTPメソッドを実装してインターフェースを実装します。ただし、構造体のユースケースはデータを保持するためのものですが、currently には状態がないため、データを作成するのは適切ではありません。HandlerFuncタイプは、通常の関数をHTTPハンドラーとして使用できるようにするアダプターです。fが適切なシグネチャを持つ関数である場合、HandlerFunc(f) はfを呼び出すハンドラーです。
HandlerFunc
がすでにServeHTTP
メソッドを実装していることがわかります。PlayerServer
関数をタイプキャストすることで、必要なHandler
を実装しました。http.ListenAndServe(":5000"...)
ListenAndServe
はリッスンするポートを Handler
に受け取ります。問題がある場合、ウェブサーバーはエラーを返します。エラーの一例として、ポートがすでにリッスンされていることが考えられます。そのため、この呼び出しを log.Fatal
でラップして、ユーザーのために、エラーをログに出力します。確かに、どのプレイヤーがどのスコアを獲得するかを制御するために、何らかのストレージの概念が必要です。テストで値が非常に恣意的に見えるのは奇妙です。
r.URL.Path
はリクエストのパスを返すので、strings.TrimPrefix
を使用して、 /players/
を削除します。要求されたプレーヤーを取得します。それほど堅牢ではありませんが、とりあえずはうまくいくでしょう。PlayerServer
を簡略化できますGetPlayerScore
に移動しました。これは、インターフェースを使用して懸念事項を分離するのに適切な場所のように感じます。PlayerServer
がPlayerStore
を使用できるようにするには、それを参照する必要があります。これで、アーキテクチャを変更して、PlayerServer
がstruct
になるようにする適切なタイミングのように感じられます。Handler
インターフェースを実装します。store.GetPlayerScore
を呼び出してスコアを取得することです。./main.go:9:58: type PlayerServer is not an expression
PlayerServer
の新しいインスタンスを作成し、そのメソッドServeHTTP
を呼び出す必要があります。main.go
はコンパイルされません。PlayerStore
を渡していないためです。スタブを1つ作成する必要があります。map
は、テスト用のスタブ キー/値(key/value)ストアを作成する迅速で簡単な方法です。次に、テスト用にこれらのストアの1つを作成して、PlayerServer
に送信します。PlayerStore
にこのデータがあるので、それをPlayerServer
で使用すると、次の応答が得られるはずであることを読者に伝えています。http://localhost:5000/players/Pepper
でサーバーにアクセスしようとすると、恐ろしい応答が返されます。PlayerStore
を渡していないためです。go build
を再度実行して同じURLにアクセスすると、"123"
が表示されます。すばらしいとは言えませんが、データを保存するまでは、私たちができる最高のことです。 また、メインのアプリケーションが起動しても実際には動かないというのも、あまり気分のいいものではありませんでした。問題を確認するために、手動でテストする必要がありました。POST /players/{name}
シナリオを処理しますPOST
シナリオは「ハッピーパス」に近づきますが、すでにそのコンテキストにいるため、最初に不足しているプレーヤーシナリオに取り組む方が簡単だと思います。残りは後で行います。StatusNotFound
を書き込むことですが、すべてのテストに成功しています!StatusOK
を取得する必要があることを表明していません。assertStatus
を作成しました。PlayerServer
を修正できます。GET /players/{name}
とは異なる方法で処理する機能を実行できます。これがうまくいったら、ハンドラーとストアの相互作用を評価し始めることができます。if
ステートメントでうまくいくことを覚えておいてください。ServeHTTP
のルーティングの側面が少し明確になり、格納に関する次の反復がprocessWin
の内部に収まるようになりますPOST /players/{name}
を実行するときに、PlayerStore
が勝利を記録するように指示されていることを確認します。StubPlayerStore
を新しいRecordWin
メソッドで拡張し、その呼び出しをスパイすることで実現できます。StubPlayerStore
を作成するコードを更新する必要がありますRecordWin
を呼び出せるようにするには、インターフェイスを変更して、PlayerStore
が何であるかについてのPlayerServer
の考えを更新する必要があります。main
はコンパイルされなくなりますInMemoryPlayerStore
を更新しましょう。PlayerStore
にRecordWin
があるので、PlayerServer
内で呼び出すことができます。"Bob"
は、RecordWin
に送信したいものではないので、テストをさらに改良してみましょう。winCalls
スライスに1つの要素があることがわかったので、最初の要素を安全に参照して、それがplayer
と等しいことを確認できます。processWin
をhttp.Request
に変更して、URLを見てプレーヤーの名前を抽出できるようにしました。それができたら、正しい値でstore
を呼び出してテストに合格することができます。main
を実行して、意図したとおりにソフトウェアを使用すると、PlayerStore
を正しく実装するためのラウンドがないため、機能しません。これは問題ありません。ハンドラーに焦点を当てることで、事前に設計するのではなく、必要なインターフェースを特定しました。InMemoryPlayerStore
の周りにいくつかのテストを書き始めることができましたが、これは、プレーヤーのスコアを永続化するためのより堅牢な方法を実装するまで一時的にのみです(つまり、データベース)。PlayerServer
とInMemoryPlayerStore
の間に 統合テスト を記述して、機能を完成させます。これにより、InMemoryPlayerStore
を直接テストする必要なく、アプリケーションが機能していると確信できるという目標を達成できます。それだけでなく、データベースでのPlayerStore
の実装に取り掛かると、同じ統合テストでその実装をテストできます。InMemoryPlayerStore
とPlayerServer
を作成しています。player
の3つの勝利を記録するために3つのリクエストを発行します。このテストのステータスコードは、それらがうまく統合されているかどうかには関係がないので、あまり心配していません。response
を格納することなので、player
のスコアを取得しようとするためです。InMemoryPlayerStore
で使用している特定のユニットの周りではありません。InMemoryPlayerStore
に関連するより具体的な単体テストを記述して、ソリューションを実行できるようにします。