Kotest ジェネレーター操作(Generator Operations)

Kotest ジェネレーター操作(Generator Operations)として、Next、Filter、Map、FlatMap、Merging、Bind を紹介する。

Next

プロパティテストでなくても、値を返すためだけに Arb を使用したい場合は、次を呼び出せばよい。

val arbA: Arb<A> = ...
val a = arbA.next() // use Random.Default
val a2 = arbA.next(rs) // pass in Random

Filter

Arb があり、値のサブセットを提供する新しい Arb を作成したい場合は、元の Arb で filter を呼び出すことができる。たとえば、偶数を生成する 1 つの方法は、整数ジェネレーターを取得して奇数値をフィルタリングすることである。

val evens = Arb.int().filter { it.value % 2 == 0 }
val odds = Arb.int().filter { it.value % 2 == 1 }

Map

Arb があり、生成された値を変換したい場合は map を使用できる。

val integerStrings: Arb<String> = Arb.int().map { it.toString() }

FlatMap

放出値またはエッジケースが以前の任意放出値に依存する arb がある場合は、flatMap を使用できる。

val dependentArbs: Arb<String> = Arb.of("foo", "bar").flatMap { prefix ->
   Arb.int(1..10).map { integer ->
      "${prefix}-${integer}"
   }
}

Merging

2 つのジェネレーターをマージして、結果要素が両方のジェネレーターから均等にサンプリングされるようにできる。

val merged = arbA.merge(arbB)

そのため、次の例では各ランダムサンプルで "A" または "B" が出る確率が同じになる。

val a = arbitrary { "a" }
val b = arbitrary { "b" }
val ab = a.merge(b)

println(ab.take(1000).groupingBy { it }.eachCount())
// {a=493, b=507}

3 つ以上の arbitrary オブジェクトをマージする場合は、Arb.choice または Arb.choose の方が適していることがある。たとえば次のように使用できる。

  • Arb.choice(arbA, arbB, arbC)arbAarbBarbC の間で均一サンプリングするために使用できる。
  • また、各 arbitrary の頻度をより細かく制御するには Arb.choose(4 to arbA, 1 to arbB, 5 to arbC) を使用できる。この例では、arbAarbBarbC がそれぞれ 40%、10%、50% の確率でサンプリングされる。

Bind

Bind は、複数の任意値を適用したい場合に便利である。bind を使ってデータクラスの値を構築する方法を見てみる。

data class Person(val name: String, val age: Int)

val personArb: Arb<Person> = Arb.bind(
   Arb.string(),
   Arb.int()
) { name, age -> Person(name, age) }

参照