diff --git a/kotest-property/src/commonMain/kotlin/io/kotest/property/config.kt b/kotest-property/src/commonMain/kotlin/io/kotest/property/config.kt index 139c9a7bbc2..32f1918e624 100644 --- a/kotest-property/src/commonMain/kotlin/io/kotest/property/config.kt +++ b/kotest-property/src/commonMain/kotlin/io/kotest/property/config.kt @@ -16,7 +16,8 @@ data class PropTest( val minSuccess: Int = Int.MAX_VALUE, val maxFailure: Int = 0, val shrinkingMode: ShrinkingMode = ShrinkingMode.Bounded(1000), - val iterations: Int? = null + val iterations: Int? = null, + val listeners: List = listOf() ) fun PropTest.toPropTestConfig() = @@ -25,7 +26,8 @@ fun PropTest.toPropTestConfig() = minSuccess = minSuccess, maxFailure = maxFailure, iterations = iterations, - shrinkingMode = shrinkingMode + shrinkingMode = shrinkingMode, + listeners = listeners ) data class PropTestConfig( @@ -33,5 +35,11 @@ data class PropTestConfig( val minSuccess: Int = Int.MAX_VALUE, val maxFailure: Int = 0, val shrinkingMode: ShrinkingMode = ShrinkingMode.Bounded(1000), - val iterations: Int? = null + val iterations: Int? = null, + val listeners: List = listOf() ) + +interface PropTestListener { + suspend fun beforeTest(): Unit = Unit + suspend fun afterTest(): Unit = Unit +} diff --git a/kotest-property/src/commonMain/kotlin/io/kotest/property/internal/proptest.kt b/kotest-property/src/commonMain/kotlin/io/kotest/property/internal/proptest.kt index dc34cba5d5e..ee83ad44875 100644 --- a/kotest-property/src/commonMain/kotlin/io/kotest/property/internal/proptest.kt +++ b/kotest-property/src/commonMain/kotlin/io/kotest/property/internal/proptest.kt @@ -23,9 +23,11 @@ suspend fun proptest( .take(iterations) .forEach { a -> val shrinkfn = shrinkfn(a, property, config.shrinkingMode) + config.listeners.forEach { it.beforeTest() } test(context, config, shrinkfn, listOf(a.value), random.seed) { context.property(a.value) } + config.listeners.forEach { it.afterTest() } } context.checkMaxSuccess(config, random.seed) return context @@ -51,9 +53,11 @@ suspend fun proptest( .take(iterations) .forEach { (a, b) -> val shrinkfn = shrinkfn(a, b, property, config.shrinkingMode) + config.listeners.forEach { it.beforeTest() } test(context, config, shrinkfn, listOf(a.value, b.value), random.seed) { context.property(a.value, b.value) } + config.listeners.forEach { it.afterTest() } } context.checkMaxSuccess(config, random.seed) @@ -81,9 +85,11 @@ suspend fun proptest( .forEach { (ab, c) -> val (a, b) = ab val shrinkfn = shrinkfn(a, b, c, property, config.shrinkingMode) + config.listeners.forEach { it.beforeTest() } test(context, config, shrinkfn, listOf(a.value, b.value, c.value), random.seed) { context.property(a.value, b.value, c.value) } + config.listeners.forEach { it.afterTest() } } context.checkMaxSuccess(config, random.seed) @@ -114,9 +120,11 @@ suspend fun proptest( val (ab, c) = abc val (a, b) = ab val shrinkfn = shrinkfn(a, b, c, d, property, config.shrinkingMode) + config.listeners.forEach { it.beforeTest() } test(context, config, shrinkfn, listOf(a.value, b.value, c.value, d.value), random.seed) { context.property(a.value, b.value, c.value, d.value) } + config.listeners.forEach { it.afterTest() } } context.checkMaxSuccess(config, random.seed) @@ -156,9 +164,11 @@ suspend fun proptest( val (ab, c) = abc val (a, b) = ab val shrinkfn = shrinkfn(a, b, c, d, e, property, config.shrinkingMode) + config.listeners.forEach { it.beforeTest() } test(context, config, shrinkfn, listOf(a.value, b.value, c.value, d.value, e.value), random.seed) { context.property(a.value, b.value, c.value, d.value, e.value) } + config.listeners.forEach { it.afterTest() } } context.checkMaxSuccess(config, random.seed) return context @@ -200,9 +210,11 @@ suspend fun proptest( val (ab, c) = abc val (a, b) = ab val shrinkfn = shrinkfn(a, b, c, d, e, f, property, config.shrinkingMode) + config.listeners.forEach { it.beforeTest() } test(context, config, shrinkfn, listOf(a.value, b.value, c.value, d.value, e.value, f.value), random.seed) { context.property(a.value, b.value, c.value, d.value, e.value, f.value) } + config.listeners.forEach { it.afterTest() } } context.checkMaxSuccess(config, random.seed) return context diff --git a/kotest-property/src/jvmTest/kotlin/com/sksamuel/kotest/property/PropListenersTest.kt b/kotest-property/src/jvmTest/kotlin/com/sksamuel/kotest/property/PropListenersTest.kt new file mode 100644 index 00000000000..d04892de298 --- /dev/null +++ b/kotest-property/src/jvmTest/kotlin/com/sksamuel/kotest/property/PropListenersTest.kt @@ -0,0 +1,126 @@ +package com.sksamuel.kotest.property + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.kotest.property.* +import io.kotest.property.arbitrary.int +import io.kotest.property.arbitrary.string + +class PropListenersTest : FunSpec({ + var previous = -1 + var current = 0 + var total = 0 + + beforeTest { + previous = -1 + current = 0 + total = 0 + } + + val propConfig = PropTestConfig(listeners = listOf(object : PropTestListener { + override suspend fun beforeTest() { + previous = current + ++current + } + + override suspend fun afterTest() { + ++total + } + })) + + test("checkAll calls listener for param A") { + val context = checkAll(10, propConfig, Arb.string()) { + current shouldBe (previous + 1) + total shouldBe previous + } + previous shouldBe 9 + current shouldBe 10 + total shouldBe 10 + } + + test("checkAll calls listener for params A, B") { + val context = checkAll( + 10, + propConfig, + Arb.string(), + Arb.int() + ) { _, _ -> + current shouldBe (previous + 1) + total shouldBe previous + } + previous shouldBe 9 + current shouldBe 10 + total shouldBe 10 + } + + test("checkAll calls listener for params A, B, C") { + val context = checkAll( + 10, + propConfig, + Arb.string(), + Arb.string(), + Arb.int() + ) { _, _, _ -> + current shouldBe (previous + 1) + total shouldBe previous + } + previous shouldBe 9 + current shouldBe 10 + total shouldBe 10 + } + + test("checkAll calls listener for params A, B, C, D") { + val context = checkAll( + 10, + propConfig, + Arb.string(), + Arb.int(), + Arb.string(), + Arb.int() + ) { _, _, _, _ -> + current shouldBe (previous + 1) + total shouldBe previous + } + previous shouldBe 9 + current shouldBe 10 + total shouldBe 10 + } + + test("checkAll calls listener for params A, B, C, D, E") { + val context = checkAll( + 10, + propConfig, + Arb.string(), + Arb.int(), + Arb.string(), + Arb.string(), + Arb.int() + ) { _, _, _, _, _ -> + current shouldBe (previous + 1) + total shouldBe previous + } + previous shouldBe 9 + current shouldBe 10 + total shouldBe 10 + } + + test("checkAll calls listener for params A, B, C, D, E, F") { + val context = checkAll( + 10, + propConfig, + Arb.string(), + Arb.int(), + Arb.string(), + Arb.int(), + Arb.string(), + Arb.int() + ) { _, _, _, _, _, _ -> + current shouldBe (previous + 1) + total shouldBe previous + } + previous shouldBe 9 + current shouldBe 10 + total shouldBe 10 + } +})