Golangテスト周りに関する覚書
基本的なテストコード
package mypackage
func MyFunction(s string) string {
return s + s
}
package mypackage
import (
"testing"
)
func TestMyFunction(t *testing.T) {
input := "わーい"
got := MyFunction(input)
want := "わーいわーい"
if got != want {
t.Errorf("got:%s want: %s", got, want)
}
}
テストの実行
全てのテストを実行
go test
パッケージ単位で実行
関数単位で実行
ファイル名を指定して実行
カバレッジの計測
ベンチマークの計測
ビルドタグによるテスト対象の制御
並列実行
テストコードを書く
前処理/後処理
並列実行
テストをスキップ
Exampleテスト
テストデータ
テストコードのパッケージ
ヘルパー関数
func helperFunction(t *testing.T, s string) string {
t.Helper() //
u, err := url.Parse(s)
if err != nil {
t.Fatal(err)
}
return u
}
func TestHello(t *testing.T) {
message := "ieeeeeee"
result := Echo(message)
expext := message
if result != expext {
t.Error("\n実際: ", result, "\n理想: ", expext)
}
t.Cleanup(func() {
t.Log("Cleanup")
})
}
T.Error
テーブル駆動テスト
非公開関数をテストする
benchmark
便利ライブラリ
参考資料
Appendix-1
type T
func (c *T) Cleanup(f func())
func (t *T) Deadline() (deadline time.Time, ok bool)
func (c *T) Error(args ...interface{})
func (c *T) Errorf(format string, args ...interface{})
func (c *T) Fail()
func (c *T) FailNow()
func (c *T) Failed() bool
func (c *T) Fatal(args ...interface{})
func (c *T) Fatalf(format string, args ...interface{})
func (c *T) Helper()
func (c *T) Log(args ...interface{})
func (c *T) Logf(format string, args ...interface{})
func (c *T) Name() string
func (t *T) Parallel()
func (t *T) Run(name string, f func(t *T)) bool
func (t *T) Setenv(key, value string)
func (c *T) Skip(args ...interface{})
func (c *T) SkipNow()
func (c *T) Skipf(format string, args ...interface{})
func (c *T) Skipped() bool
func (c *T) TempDir() string
T.Cleanup
- 全てのテストが完了した際に呼び出される関数を登録する
func TestHello(t *testing.T) {
message := "ieeeeeee"
result := Echo(message)
expext := message
if result != expext {
t.Error("\n実際: ", result, "\n理想: ", expext)
}
t.Cleanup(func() {
t.Log("Cleanup")
})
}
Appendix-2 テストコードサンプル
// テスト関数
func Test(t *testing.T) {
// テスト対象の初期化関数
initModel := func() *Model {
return &Model{
Name: "TEST"
Age: 0
}
}
tests := []struct {
name string // テストケース名
model func() *Model // テスト対象を加工する関数
want bool // 期待値
}{
{
name: "testCase_1",
model: func() *Model {
// ベースとなるモデルを取得
model := initModel()
// テスト内容に応じて加工
model.Age = 18
// 返却
return model
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// テスト対象を取得
model := tt.model()
// 検証
got := model.IsAdult()
if got != tt.want {
t.Error("Error")
}
})
}
}
-
initModel
はテストで使うモジュールの初期化を行う
- テスト結果に直接影響しないが、設定しておかないとテストが失敗する項目など
-
tests
にはでモジュールの加工を行う関数を設けておき、テスト内容に応じてinitModel
で初期化したモジュールのデータを加工する
-
initModel
で毎度モジュールのインスタンスを生成しているのは、変数を共用した場合、構造体内部でポインタ型が使用されているとあるテストの設定が別のテストに影響を与える恐れがあるからである