Kotest Generator Operations
Next
If you want to use an Arb only to return values, even outside a property test, call the following:
val arbA: Arb<A> = ...
val a = arbA.next() // use Random.Default
val a2 = arbA.next(rs) // pass in Random
Filter
If you have an Arb and want to create a new Arb that provides a subset of values, you can call filter on the source Arb. For example, one way to generate even numbers is to take an integer generator and filter out odd values.
val evens = Arb.int().filter { it.value % 2 == 0 }
val odds = Arb.int().filter { it.value % 2 == 1 }
Map
If you have an Arb and want to transform generated values, you can use map.
val integerStrings: Arb<String> = Arb.int().map { it.toString() }
FlatMap
If you have an arb whose emissions or edge cases depend on a previous arbitrary emission, you can use flatMap.
val dependentArbs: Arb<String> = Arb.of("foo", "bar").flatMap { prefix ->
Arb.int(1..10).map { integer ->
"${prefix}-${integer}"
}
}
Merging
You can merge two generators so that result elements are sampled equally from both generators.
val merged = arbA.merge(arbB)
Therefore, in the following example, "A" or "B" has the same probability of appearing in each random sample:
val a = arbitrary { "a" }
val b = arbitrary { "b" }
val ab = a.merge(b)
println(ab.take(1000).groupingBy { it }.eachCount())
// {a=493, b=507}
If you are merging more than two arbitrary objects, Arb.choice or Arb.choose may be more appropriate. For example:
Arb.choice(arbA, arbB, arbC)can be used for uniform sampling amongarbA,arbB, andarbC.- Or, for finer control over the frequency of each arbitrary, use
Arb.choose(4 to arbA, 1 to arbB, 5 to arbC). In this example,arbA,arbB, andarbCare sampled with probabilities of 40%, 10%, and 50%, respectively.
Bind
Bind is useful when you want to apply several arbitrary values. You can see how bind constructs values for a data class.
data class Person(val name: String, val age: Int)
val personArb: Arb<Person> = Arb.bind(
Arb.string(),
Arb.int()
) { name, age -> Person(name, age) }