Go言語(golang)のtestingパッケージを使ったテスト
Go言語のテストには、testingパッケージが用意されていて、
goのサブコマンド $ go test
でテストを実行します。
また、サーバーが必要なテストには httptestパッケージを使います。
目次
Go言語のassert
Go言語には assert はありません。if 文を使ってテストします。
サードパーティのパッケージは testify や goconvey がスターが多い。
基本的なテストの書き方
- テスト対象の .go ファイル
hello.go
package hello func hello(name string) string { return "hello, " + name }
- テストの記述方法
テスト対象のファイル名に _test
をつけるのがテストファイルの命名規則になっている。
テスト関数の関数名は、Test
で始めて、testing.T
を引数で受け取り、
t.Error, t.Fatal
などで、エラーの場合にメッセージを出力する
hello_test.go
package hello import "testing" func TestHello(t *testing.T) { expect := "hello, Tanaka" actual := hello("Tanaka") if actual != expect { t.Errorf("actual: %v, expected: %v", actual, expect) } }
- テストの実行
$ ls hello.go hello_test.go // エラーがあれば FAIL でそのエラーメッセージが表示される $ go test PASS ok _/hello 0.001s
テスト実行のコマンドオプション
- 一部のファイルのみテストを実行する
$ ls bar.go bar_test.go baz.go baz_test.go foo.go foo_test.go $ go test foo.go foo_test.go
- 詳細表示(パスしたテスト名も表示)
$ go test -v
- カバレッジを表示
$ go test -cover
- 関数名を指定して一部のテストのみ実行する(正規表現が使える)
$ go test -run TestHello
- 現在のディレクトリ以下のテストを再帰的に実行する
$ go test ./...
- src以下のすべてのテストを実行する
$ go test ...
オプション
- -bench regexp
- -benchmem
- -benchtime t
- -blockprofile block.out
- -blockprofilerate n
- -cover
- -covermode set,count,atomic
- -coverpkg pkg1,pkg2,pkg3
- -coverprofile cover.out
- -cpu 1,2,4
- -cpuprofile cpu.out
- -memprofile mem.out
- -memprofilerate n
- -outputdir directory
- -parallel n
- -run regexp
- -short
- -timeout t
- -v
ドキュメントにExampleを表示する
GoDoc は、GitHubなどで公開したGoパッケージのドキュメントを掲載できるサービスです。
掲載する方法は、検索ボックスでリポジトリの URL を検索するだけです。自動的にドキュメントが生成されます。
その公開された、ドキュメントの対応する箇所に コードの書き方などの例 を表示するために書くのが example_test です。
example_test.go
package hello_test // ここのコメントも意味があって、コード例の内容の説明に使う func Example() { // 全体のコード使用例や説明でドキュメントの先頭に表示される } // this example shows how to use Hello func ExampleHello() { // Hello 関数に関する使用例を書く } // this example shows how to use Hello func ExampleString_Hello() { // 関数の引数の型など Type_ で書く }
一つの関数に複数のテストを書く
package hello import ( "testing" ) func TestHello(t *testing.T) { t.Run("hello-1", func(t *testing.T) { t.Fatal("error") }) t.Run("hello-2", func(t *testing.T) { t.Fatal("error") }) t.Run("hello-3", func(t *testing.T) { t.Fatal("error") }) }
特定のテストのみ実行するのと同じように、TestHelloの hello-2
のみテストすることもできる
$ go test -run TestHello/hello-2
テストをスキップする
何らかの理由(時間が掛かるなど)でテストを飛ばす場合は、testing.Short
を使う
package hello import ( "testing" ) func TestHello(t *testing.T) { if testing.Short() { // スキップ時のメッセージ t.Skip("skipping this test") } if 0 != 1 { t.Fatal("error") } }
テストの実行(-shortでスキップする)
$ go test -short -v === RUN TestHello --- SKIP: TestHello (0.00s) hello_test.go:9: skipping this test PASS ok _/hello 0.001s
サーバーが必要なテスト
サーバーを使ってテストを行いたい場合は、httptest を使います。
以下の例では、テストの前処理と後処理を行えるように、TestMain
を使っています。
TLSサーバーでテストしたい場合は httptest.NewTLSServer
を使います。
package hello import ( "io/ioutil" "net/http" "net/http/httptest" "os" "testing" ) var ( server *httptest.Server ) func TestMain(m *testing.M) { server = httptest.NewServer(nil) // ↑ 前処理 ↑ exitcode := m.Run() // m.Run でテストを実行し、終了コードを受け取る // ↓ 後処理 ↓ server.Close() // サーバーを閉じる os.Exit(exitcode) } func handler(w http.ResponseWriter, r *http.Request) { w.Write([]byte("hello")) } func TestHello(t *testing.T) { server.Config.Handler = http.HandlerFunc(handler) resp, _ := http.Get(server.URL) // server.URL でURLを取れる body, _ := ioutil.ReadAll(resp.Body) if string(body) != "hello" { t.Errorf("body: %s", string(body)) } }