如何在 Kotest 的另一台发电机中使用一台发电机的输出?
How to use output from one generator in another generator in Kotest?
使用 Clojure test.check let
generator 中的示例,生成一个非空字符串列表,将该列表提供给另一个生成器以从中选择一个字符串,然后创建一个包含该字符串列表和所选字符串的映射.在 Clojure 中,它看起来如下:
(gen/let [list-of-strings (gen/not-empty (gen/list gen/string))
a-string (gen/element list-of-strings)] ;; use the generated list-of-strings above
{:all-strings list-of-strings
:selected a-string})
受io.kotest.property.arbitrary.bind
启发,我试过如下实现,但是不行(Kotlin编译器吐出来"Type inference failed"):
fun <A, B, T: Any> let(genA: Gen<A>, genB: (A) -> Gen<B>, bindFn: (A, B) -> T): Arb<T> {
return arb { rs ->
val iterA = genA.generate(rs).iterator()
generateSequence {
val a = iterA.next()
val iterB = genB(a.value).generate(rs).iterator()
val b = iterB.next()
bindFn(a.value, b.value)
}
}
}
原来删除 bindFn
参数解决了问题,但解决方案看起来有点难看,因为它需要 return 一个 Pair
:
fun <A, B> let(genA: Gen<A>, genBFn: (A) -> Gen<B>): Arb<Pair<A, B>> {
return arb { rs ->
val iterA = genA.generate(rs).iterator()
generateSequence {
val a = iterA.next().value
// could combine the following to one line, but split for clarity
val genB = genBFn(a)
val iterB = genB.generate(rs).iterator()
Pair(a, iterB.next().value)
}
}
}
然后结合上面,使用起来如下:
class StringTest : StringSpec({
"element is in list" {
val letGen = let(
Arb.list(Arb.string(), range=1..100), // genA
{ xs -> Arb.element(xs) } // genBFn
)
forAll(letGen) { (xs, x) ->
x in xs
}
}
})
从上面的解决方案中得到启发,写了一个更短的解决方案
fun <A, B> Gen<A>.then(genFn: (A) -> Gen<B>): Arb<Pair<A, B>> =
arbitrary { rs ->
val first = this.generate(rs).first().value
val second = genFn(first).generate(rs).first().value
Pair(first, second)
}
class StringTest : StringSpec({
"element is in list" {
val dependArb =
Arb.list(Arb.string(), range=1..100).then { Arb.element(it) } // genBFn
forAll(dependArb) { (xs, x) ->
x in xs
}
}
})
使用 Clojure test.check let
generator 中的示例,生成一个非空字符串列表,将该列表提供给另一个生成器以从中选择一个字符串,然后创建一个包含该字符串列表和所选字符串的映射.在 Clojure 中,它看起来如下:
(gen/let [list-of-strings (gen/not-empty (gen/list gen/string))
a-string (gen/element list-of-strings)] ;; use the generated list-of-strings above
{:all-strings list-of-strings
:selected a-string})
受io.kotest.property.arbitrary.bind
启发,我试过如下实现,但是不行(Kotlin编译器吐出来"Type inference failed"):
fun <A, B, T: Any> let(genA: Gen<A>, genB: (A) -> Gen<B>, bindFn: (A, B) -> T): Arb<T> {
return arb { rs ->
val iterA = genA.generate(rs).iterator()
generateSequence {
val a = iterA.next()
val iterB = genB(a.value).generate(rs).iterator()
val b = iterB.next()
bindFn(a.value, b.value)
}
}
}
原来删除 bindFn
参数解决了问题,但解决方案看起来有点难看,因为它需要 return 一个 Pair
:
fun <A, B> let(genA: Gen<A>, genBFn: (A) -> Gen<B>): Arb<Pair<A, B>> {
return arb { rs ->
val iterA = genA.generate(rs).iterator()
generateSequence {
val a = iterA.next().value
// could combine the following to one line, but split for clarity
val genB = genBFn(a)
val iterB = genB.generate(rs).iterator()
Pair(a, iterB.next().value)
}
}
}
然后结合上面,使用起来如下:
class StringTest : StringSpec({
"element is in list" {
val letGen = let(
Arb.list(Arb.string(), range=1..100), // genA
{ xs -> Arb.element(xs) } // genBFn
)
forAll(letGen) { (xs, x) ->
x in xs
}
}
})
从上面的解决方案中得到启发,写了一个更短的解决方案
fun <A, B> Gen<A>.then(genFn: (A) -> Gen<B>): Arb<Pair<A, B>> =
arbitrary { rs ->
val first = this.generate(rs).first().value
val second = genFn(first).generate(rs).first().value
Pair(first, second)
}
class StringTest : StringSpec({
"element is in list" {
val dependArb =
Arb.list(Arb.string(), range=1..100).then { Arb.element(it) } // genBFn
forAll(dependArb) { (xs, x) ->
x in xs
}
}
})