My External Storage

Sep 5, 2018 - 3 minute read - Comments - go

Goのtestingを理解する in 2018 - quickサブパッケージ編 #go

この記事は以下の記事で触れなかった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関数が戻すErrorquick.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関数が戻すErrorquick.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の引数に利用できる。 - https://play.golang.org/p/8HRbhNYBsnu

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)))
}

参考

関連

関連記事