Go
の標準ライブラリであるtesting
を使ってテストを書くお話です。
いわゆる単体テストと呼ばれるレベルでのテストを書いていきます。
テストするコード
テストコードを書くお題には次のような関数を使いました。
package main
import (
"fmt"
)
func showColor(fruit string) (string, error) {
fruitBasket := map[string]string{
"banana": "yellow",
"apple": "red",
"melon": "green",
}
if color, ok := fruitBasket[fruit]; ok {
return color, nil
} else {
return color, fmt.Errorf("%s does not exist in Fruit Basket", fruit)
}
}
果物の名前を放り込むと何色かを返してくれる関数ですね。(フルーツバスケット的な)
関数の中で定義されているfruitBasket
に存在しない果物の名前に対してはエラーを返すようにしています。
テストを書く
Go
のお作法ではcolor.go
のテストコードはcolor_test.go
というファイルに記述します。
package main
import (
"testing"
)
func Test_showColor(t *testing.T) {
tests := []struct {
name string
fruit string
wantColor string
wantErr bool
}{
{
name: "正常系1 バナナ",
fruit: "banana",
wantColor: "yellow",
wantErr: false,
},
{
name: "正常系2 りんご",
fruit: "apple",
wantColor: "red",
wantErr: false,
},
{
name: "正常系3 メロン",
fruit: "melon",
wantColor: "green",
wantErr: false,
},
{
name: "異常系 いちご",
fruit: "strawberry",
wantColor: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actualColor, err := showColor(tt.fruit)
if tt.wantErr {
// tt.wantErr が true つまりちゃんとエラーが発生することを確認するための分岐
if err == nil {
// エラーが発生するはずなのに err == nil つまりコードかテストケースにバグがある
t.Fatalf("%q. wantErr %v, but actual err %v", tt.name, tt.wantErr, err)
}
} else if err != nil {
// 正常終了するはずなのにエラーが発生 同じくコードかテストコードにバグがある
t.Fatalf("%q. wantErr %v, but actual err occured %+v", tt.name, tt.wantErr, err)
} else {
// 関数が正常終了したので返り値が期待通りか検証する
if tt.wantColor != actualColor {
t.Fatalf("%q. expected color %v, but actual %v", tt.name, tt.wantColor, actualColor)
}
}
})
}
}
Go
にはassert
のような関数がないため、t.Run()
でテストを走らせた後に自分で一つ一つ値を検証していくことになります。
テストを実行する
下記のコマンドでテストを実行できます。
FAIL
した場合、テストコードか元のコードに問題があるため、何故FAILしたのかを把握しやすいようにログメッセージを書いておくのがコツです。
$ go test
PASS
ok github.com/hogehoge/go-testing-sample 0.005s
(以下、FAILした場合の出力)
--- FAIL: Test_showColor (0.00s)
--- FAIL: Test_showColor/正常系3_メロン (0.00s)
color_test.go:55: "正常系3 メロン". expected color pink, but actual green
FAIL
exit status 1
FAIL github.com/hogehoge/go-testing-sample 0.006s
上記のコマンドだとGo
で書かれたテストコードがすべて実行されてしまいます。
任意のテストだけ動かしたい場合には下記のように実行します。今回であればTest_showColor
のみ実行したいため...
$ go test -run Test_showColor
PASS
ok github.com/hogehoge/go-testing-sample 0.005s
カバレッジの確認
下記のようにテストを実行すると、実行結果とともにテストコードのカバレッジも出力してくれます。
$ go test -cover
PASS
coverage: 100.0% of statements
ok github.com/hogehoge/go-testing-sample 0.008s
ちなみにコードのどの部分までカバーできているのかを確認するには下記のようにします。
$ go test -coverprofile cover.out
PASS
coverage: 100.0% of statements
ok github.com/hogehoge/go-testing-sample 0.005s
テストを実行したディレクトリ上にcover.out
というファイルが作成されたかと思います。
こいつを下記コマンドで開きます。
$ go tool cover -html=cover.out
ブラウザが立ち上がり、下記のような画面が表示されたかと思います。
緑の部分がcovered
、つまりテストコードで検証されている部分です。not covered
は赤色で表示されます。
(今回はカバレッジ100%になりましたが、実際の開発の現場ではカバレッジ100%を目指すことは非常に難しいですし、ナンセンスです)
番外編:一連のテスト実行をMakefileにまとめる
Makefile
で下記のように記述すれば、テストの実行、カバレッジの表示、ブラウザ表示までを一気に実行可能です。
test:
go test -coverprofile cover.out &&\
go tool cover -html=cover.out
前述のコマンドを順番に実行するように記述しただけですが、いちいちプロンプトに入力する手間が省けるのでご参考までに。
ちなみに&&
は直前のコマンドがstatus 0
で終了した場合に後続のコマンドを実行してくれるものです。
つまり、go test ~~
がエラーなく完了できた場合のみ、後続のgo too: ~~
を実行してくれます。これにより、Makefile
では無限にコマンドをつなげる事が可能になります(あんまりやりませんが...)
なお、Makefile
内でまとめて実行したいコマンド群を改行する際には\(バックスラッシュ)
を使います。
(おしまい)