この記事は以下の記事で触れなかったtesting/quick
パッケージについてまとめる。
TL;DR
- testing/quickパッケージ
testing/quick
パッケージはランダムテストを行う- 新旧メソッドを比較するデグレ確認にも使うことができる。
quick.CheckEqual関数
func Check(f interface{}, config *Config) error
は与えられた関数f
に対してランダムテストを行う。
f
の引数が構造体や文字列でもある程度は融通してランダムな引数を用意してくれる。
また、f
は戻り値がbool
である必要があるので、必要に応じてラップ関数を作る。
type MyInt struct {
V int
X string
}
func div(n, m MyInt) int {
return n.V / m.V
}
func TestDiv(t *testing.T) {
// div関数は戻り値がintなので、評価結果をboolで返すラップ関数を用意する
f := func(x, y MyInt) bool {
return div(x, y) == (x.V / y.V)
}
if err := quick.Check(f, nil); err != nil {
t.Error(err.Error())
}
}
第二引数のquick.Config
を指定すれば、試行回数や与える引数の範囲、ランダムシードなどを設定できる。
cfg := &quick.Config{
// 試行回数
MaxCount: 10,
// Seed
Rand: rand.New(rand.NewSource(2)),
// 独自定義した引数の生成関数
Values: func(args []reflect.Value, rand *rand.Rand) {
args[0] = reflect.ValueOf(MyInt{rand.Int(), "x"})
args[1] = reflect.ValueOf(MyInt{rand.Int(), "y"})
},
}
if err := quick.Check(f, cfg); err != nil {
t.Error(err.Error())
}
quick.CheckError
quick.Check
関数が戻すError
はquick.CheckError
型のポインタなので、任意の情報も取得できる。
if err := quick.Check(f, nil); err != nil {
if ce, ok := err.(*quick.CheckError); ok {
t.Errorf("Try count = %d, In %#v, Out %s\n", ce.Count, ce.In, ce)
} else {
t.Error(err)
}
}
quick.CheckEqual
quick.CheckEqual
関数は2つの関数に対して乱数テストを実施し、出力結果に変化がないか確認をできる。
Go本体のコードでは例えばsync.Map
の挙動の確認に利用されている。
func TestMapMatchesRWMutex(t *testing.T) {
if err := quick.CheckEqual(applyMap, applyRWMutexMap, nil); err != nil {
t.Error(err)
}
}
func TestMapMatchesDeepCopy(t *testing.T) {
if err := quick.CheckEqual(applyMap, applyDeepCopyMap, nil); err != nil {
t.Error(err)
}
}
quick.CheckEqualError
quick.CheckEqual
関数が戻すError
もquick.CheckEqualError
として定義されているので、結果が不一致なときの各情報を取得できる。
type CheckEqualError struct {
CheckError
Out1 []interface{}
Out2 []interface{}
}
quick.Value
ただ単にランダムな値が欲しい場合は、quick.Value
関数で生成できる。
// 乱数整数を生成する
ri, _ := quick.Value(reflect.TypeOf([]int{}), rand.New(rand.NewSource(0)))
// 乱数整数スライスを生成する
ris, _ := quick.Value(reflect.TypeOf([]int{}), rand.New(rand.NewSource(0)))
Generatorインターフェース
Generator
インターフェースを実装していれば、独自定義した型もquick.Value
の引数に利用できる。
type Foo struct{}
func (f *Foo) Generate(rand *rand.Rand, size int) reflect.Value {
// 雑な実装
return reflect.ValueOf(&Foo{})
}
func main() {
v, ok := quick.Value(reflect.TypeOf([]*Foo{}), rand.New(rand.NewSource(0)))
}
参考
- GoでQuickCheckをする
- Goでテストを書く(テストの実装パターン集) - QuickCheck(testing/quick)でブラックボックステストをする
- Goのtesting/quickを簡単に触ってみる