Golangでテストコードに入門してみた
今回はGolangでのテストコードに入門してみました。私自身普段あまりGolangは使ってこなかったのですが、今回使う気概があり、テストコードが必要だったので入門してみました。
早速やってみる
シンプルな例で。。。
まずはプロジェクトの初期化をします。goenv
を使っているので、以下のようにして環境を立てました。
mkdir go_test_prac && cd go_test_prac
goenv local 1.22.3
まずはシンプルな四則演算を実装しているコードを作成しました。
package main
import "fmt"
func Add(x int, y int) (int, error) {
return x + y, nil
}
func Sub(x int, y int) (int, error) {
return x - y, nil
}
func Mul(x int, y int) (int, error) {
return x * y, nil
}
func Div(x int, y int) (float64, error) {
if y == 0 {
return 0, fmt.Errorf("division by zero")
}
return float64(x) / float64(y), nil
}
このコードはシンプルな四則演算を実装しており、Div
関数では0割を検知するための機能を入れています。
こちらに対応するテストコードを以下のように実装しました。
package main
import "testing"
func TestAdd(t *testing.T) {
x := 1
y := 2
expected := x + y
result, _ := Add(x, y)
if result != expected {
t.Errorf("Add(1, 2) = %d; want %d", result, expected)
}
}
func TestSub(t *testing.T) {
x := 1
y := 2
expected := x - y
result, _ := Sub(x, y)
if result != expected {
t.Errorf("Sub(1, 2) = %d; want %d", result, expected)
}
}
func TestMul(t *testing.T) {
x := 1
y := 2
expected := x * y
result, _ := Mul(x, y)
if result != expected {
t.Errorf("Mul(1, 2) = %d; want %d", result, expected)
}
}
func TestDiv(t *testing.T) {
x := 1
y := 2
expected := float64(x) / float64(y)
result, _ := Div(x, y)
if result != expected {
t.Errorf("Div(1, 2) = %f; want %f", result, expected)
}
}
func TestDivZeroDivision(t *testing.T) {
x := 1
y := 0
_, err := Div(x, y)
if err == nil {
t.Error("0 division must be raised")
}
}
計算結果が正しく帰ってくることを確認する実装になっており、Div
関数において分母の数字が0とそうでない場合とで別に実装しています。
このコードをgo test ./...
で実行すると、以下のような結果が得られました。
# go test ./...
ok go_test_prac 0.296s
これより、テスト全てが正常終了し、所要時間は0.296秒であったということです。この四則演算レベルのテストはすごいシンプルなので簡単な実装で済みました。
パラメータでテストケースを複数作成
先ほどのテストケースでは、テストごとにパラメータセットを一つずつ設定していましたが、複数のパラメータセットでテストを動かしたい場合、以下のようにするとできるようです。
func TestAddMultipleCases(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"2+3", 2, 3, 5},
{"0+0", 0, 0, 0},
{"-1+1", -1, 1, 0},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result, _ := Add(test.a, test.b)
if result != test.expected {
t.Errorf("Add(%d, %d) = %d; want %d", test.a, test.b, result, test.expected)
}
})
}
}
上記のように、構造体でパラメータセットを定義してそれをfor分で回しながらテストを実行することで、パラメータを複数ペア用意してテストが実行されます。これを実行すると無事テストが成功します。ちなみに、テスト実行時にgo test -v
のように-v
オプションをつけると結果でテストケース名などが取得できます。
# go test -v ./...
=== RUN TestAdd
--- PASS: TestAdd (0.00s)
=== RUN TestSub
--- PASS: TestSub (0.00s)
=== RUN TestMul
--- PASS: TestMul (0.00s)
=== RUN TestDiv
--- PASS: TestDiv (0.00s)
=== RUN TestDivZeroDivision
--- PASS: TestDivZeroDivision (0.00s)
=== RUN TestAddMultipleCases
=== RUN TestAddMultipleCases/2+3
=== RUN TestAddMultipleCases/0+0
=== RUN TestAddMultipleCases/-1+1
--- PASS: TestAddMultipleCases (0.00s)
--- PASS: TestAddMultipleCases/2+3 (0.00s)
--- PASS: TestAddMultipleCases/0+0 (0.00s)
--- PASS: TestAddMultipleCases/-1+1 (0.00s)
PASS
ok go_test_prac 0.188s
このように、新しく追加したテストケースはテスト内で複数のテストを回していますが、それが正確に記録されていることが確認できました。
カバレッジのチェック
go test
でカバレッジをチェックするには-c
オプションをつければいいようです。また、-coverprofile=cover.out
のようにすることでカバレッジレポートをcover.out
に出力できるようです。また、go tool cover -html=cover.out
のようにすることでブラウザ上でカバレッジチェックができるようになります。試しに、func_test.go
からSub
関数に関するテストを削除する前とあとで比較してみました。期待通り、Sub
関数のテストを省略した場合のカバレッジが低くなり、それに合わせてUI上で確認できる結果でもSub
関数のテストがされていないことが確認できます。
-
Sub
関数のテストあり:coverage=85.7%
-
Sub
関数のテストなし:coverage=71.4%
まとめ
今回はGolangを使ってテストを実装するということに入門してみました。特にパラメータのペアを構造体で定義してループして実行する部分は、pytest.mark.parametrize
と似たような感じでできて、Pythonの経験が割とそのまま生きた気がします。今後はもっと複雑な機能についてもテスト実装をしてみたいと思います。
Discussion
Subテストありのカバレッジ画像がなぜか表示されないですが、二つ目の画像で赤くなっている場所が全て緑色になっている画像となります