Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11738] ファイルの概要

このコミットは、Go言語の標準ライブラリである net/http/httptest パッケージにテストケースを追加するものです。具体的には、httptest.NewServer の基本的な機能を確認するためのテストが server_test.go に追加されています。

コミット

commit ce57ba9feec078191db2873017bd63f996afd835
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Thu Feb 9 16:45:24 2012 +1100

    net/http/httptest: add a test
    
    Less ironic. Don't you think?
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5643069

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/ce57ba9feec078191db2873017bd63f996afd835

元コミット内容

--- a/src/pkg/net/http/httptest/server_test.go
+++ b/src/pkg/net/http/httptest/server_test.go
@@ -0,0 +1,29 @@
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package httptest
+
+import (
+	"io/ioutil"
+	"net/http"
+	"testing"
+)
+
+func TestServer(t *testing.T) {
+	ts := NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Write([]byte("hello"))
+	}))
+	defer ts.Close()
+	res, err := http.Get(ts.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	got, err := ioutil.ReadAll(res.Body)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if string(got) != "hello" {
+		t.Errorf("got %q, want hello", string(got))
+	}
+}

変更の背景

このコミットは、Go言語の net/http/httptest パッケージにテストを追加するものです。httptest パッケージは、HTTPサーバーやクライアントのテストを容易にするためのユーティリティを提供します。特に httptest.NewServer は、実際のHTTPサーバーを起動せずに、テスト用のHTTPサーバーをメモリ上に構築し、そのURLを提供することで、HTTPハンドラやクライアントの動作を検証できるようにします。

コミットメッセージの "Less ironic. Don't you think?" (皮肉が少ない。そう思わない?) という表現は、httptest パッケージ自体がテストを目的としているにもかかわらず、そのパッケージ自身のテストが不足していたことに対する言及であると考えられます。つまり、テストを支援するツールが、それ自身のテストを欠いているという状況を「皮肉」と表現し、その状況を改善するためにテストを追加した、という背景が読み取れます。

Go言語の初期段階では、標準ライブラリの各パッケージが着実に機能追加され、それに伴いテストカバレッジも拡充されていました。このコミットも、net/http/httptest パッケージの堅牢性を高め、将来的な変更に対する安全性を確保するための一環として行われたものと推測されます。

前提知識の解説

このコミットを理解するためには、以下のGo言語の基本的な概念とHTTPに関する知識が必要です。

  1. Go言語のテストフレームワーク (testing パッケージ):

    • Go言語には、標準で testing パッケージが用意されており、これを使ってユニットテストやベンチマークテストを記述します。
    • テスト関数は TestXxx(*testing.T) の形式で定義され、go test コマンドで実行されます。
    • *testing.T オブジェクトは、テストの失敗を報告したり、ログを出力したりするためのメソッド(例: t.Fatal(), t.Errorf())を提供します。
    • defer キーワードは、関数の実行が終了する直前に指定された関数を呼び出すために使用されます。これはリソースのクリーンアップ(例: ファイルのクローズ、サーバーのシャットダウン)によく使われます。
  2. HTTPハンドラ (http.Handler インターフェースと http.HandlerFunc):

    • Go言語の net/http パッケージでは、HTTPリクエストを処理するロジックを http.Handler インターフェースとして定義します。このインターフェースは ServeHTTP(ResponseWriter, *Request) メソッドを一つだけ持ちます。
    • http.HandlerFunc は、関数を http.Handler インターフェースに適合させるためのアダプターです。これにより、通常の関数をHTTPハンドラとして使用できるようになります。
    • http.ResponseWriter は、HTTPレスポンスをクライアントに書き込むためのインターフェースです。
    • *http.Request は、クライアントからのHTTPリクエストに関する情報(URL、ヘッダー、ボディなど)を保持する構造体です。
  3. net/http/httptest パッケージ:

    • このパッケージは、HTTPサーバーやクライアントのテストを容易にするためのユーティリティを提供します。
    • httptest.NewServer(handler http.Handler): 指定された http.Handler を使用して、テスト用のHTTPサーバーを起動します。このサーバーは実際のネットワークポートをリッスンしますが、テストの終了時に自動的にクリーンアップされます。戻り値の *httptest.Server オブジェクトは、サーバーのURL (ts.URL) やクリーンアップのための Close() メソッドを提供します。
    • httptest.Server.Close(): テストサーバーをシャットダウンし、リソースを解放します。通常は defer を使って呼び出されます。
  4. io/ioutil パッケージ:

    • ioutil.ReadAll(r io.Reader): io.Reader からすべてのデータを読み込み、[]byte スライスとして返します。HTTPレスポンスボディの読み込みによく使われます。

技術的詳細

このコミットで追加されたテスト TestServer は、net/http/httptest パッケージの NewServer 関数が正しく動作するかどうかを検証します。

テストの基本的な流れは以下の通りです。

  1. テストサーバーの起動:

    • ts := NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello")) }))
    • httptest.NewServer を呼び出してテストサーバーを起動します。
    • 引数には http.Handler インターフェースを実装したオブジェクトを渡す必要があります。ここでは、匿名関数を http.HandlerFunc でラップしてハンドラとして使用しています。
    • このハンドラは、どのようなリクエストが来ても、レスポンスボディに "hello" という文字列を書き込むだけのシンプルなものです。
    • ts*httptest.Server 型のオブジェクトで、テストサーバーに関する情報(例: サーバーのURL)を含んでいます。
  2. サーバーのクリーンアップ設定:

    • defer ts.Close()
    • defer ステートメントにより、TestServer 関数が終了する際に ts.Close() が呼び出され、テストサーバーが適切にシャットダウンされ、使用されたネットワークポートなどのリソースが解放されることを保証します。これにより、テストが完了した後にリソースがリークするのを防ぎます。
  3. HTTPリクエストの送信:

    • res, err := http.Get(ts.URL)
    • http.Get 関数を使用して、起動したテストサーバーのURL (ts.URL) に対してGETリクエストを送信します。
    • ts.URL は、httptest.NewServer が内部的に割り当てたランダムなポート番号を含む、テストサーバーの完全なURL文字列です(例: http://127.0.0.1:12345)。
    • エラーが発生した場合 (err != nil) は、t.Fatal(err) を呼び出してテストを即座に失敗させます。
  4. レスポンスボディの読み込み:

    • got, err := ioutil.ReadAll(res.Body)
    • http.Get から返された *http.Response オブジェクトの Body フィールド(io.ReadCloser 型)から、ioutil.ReadAll を使ってレスポンスボディの内容をすべて読み込みます。
    • ここでもエラーチェックを行い、エラーがあればテストを失敗させます。
  5. 結果の検証:

    • if string(got) != "hello" { t.Errorf("got %q, want hello", string(got)) }
    • 読み込んだレスポンスボディ (got) を文字列に変換し、期待される値 "hello" と比較します。
    • もし一致しない場合は、t.Errorf() を呼び出してテストの失敗を報告します。t.Errorf() はテストを即座に終了させず、他のテストコードの実行を継続させます。

このテストは、httptest.NewServer が提供するURLにHTTPリクエストを送信し、ハンドラが期待通りにレスポンスを返すことを確認する、非常に基本的なエンドツーエンドテストの例となっています。

コアとなるコードの変更箇所

このコミットでは、src/pkg/net/http/httptest/server_test.go という新しいファイルが追加されています。

// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package httptest

import (
	"io/ioutil"
	"net/http"
	"testing"
)

func TestServer(t *testing.T) {
	ts := NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("hello"))
	}))
	defer ts.Close()
	res, err := http.Get(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	got, err := ioutil.ReadAll(res.Body)
	if err != nil {
		t.Fatal(err)
	}
	if string(got) != "hello" {
		t.Errorf("got %q, want hello", string(got))
	}
}

コアとなるコードの解説

追加された server_test.go ファイルは、httptest パッケージのテストファイルです。

  • package httptest: このファイルが httptest パッケージの一部であることを示します。テストファイルは通常、テスト対象のパッケージと同じパッケージ名を使用します。
  • import ステートメント:
    • "io/ioutil": レスポンスボディを読み込むために使用します。
    • "net/http": HTTPクライアント(http.Get)やHTTPハンドラ(http.HandlerFunc)のために使用します。
    • "testing": Go言語のテストフレームワークです。
  • func TestServer(t *testing.T):
    • Go言語のテスト関数は Test で始まり、*testing.T 型の引数を取ります。
    • この関数内で、httptest.NewServer を使ってテスト用のHTTPサーバーをセットアップし、そのサーバーに対してHTTPリクエストを送信し、レスポンスを検証する一連の処理が行われます。
    • t.Fatal(err) は、エラーが発生した場合にテストを即座に終了させ、エラーメッセージを出力します。
    • t.Errorf(...) は、検証が失敗した場合にエラーメッセージを出力しますが、テストの実行は継続します。

このコードは、httptest.NewServer が提供するテストサーバーが、与えられたハンドラに従ってHTTPリクエストを処理し、期待されるレスポンスを返すことをシンプルかつ効果的に検証しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコードリポジトリ (GitHub)
  • Go言語のコミット履歴
  • 一般的なHTTPプロトコルに関する知識
  • Go言語のテストに関する一般的なプラクティス