huehnerlady
09/02/2025, 6:56 AMOliver.O
09/02/2025, 10:28 AMhuehnerlady
09/02/2025, 10:33 AMbeforeTest
On top of that if we have an object that gets manipulated by the test, we have to change val to var and instantiate it in beforeTest
lateinit var something: MyObject
beforeTest {
something = MyObject("will be changed in test)
}
in comparison to
val something = MyObject("will be changed in test)
huehnerlady
09/02/2025, 10:34 AMOliver.O
09/02/2025, 10:37 AMOliver.O
09/02/2025, 2:05 PMShervin
09/02/2025, 2:57 PMOliver.O
09/02/2025, 3:04 PMTasuku Nakagawa
09/02/2025, 3:14 PMCould someone please motivate why this has to go?you can find the reason from this commit message; Kotest team seemed necessary to rework existing Kotest engines and isolation modes to achieve more robust concurrency with simple implementation https://github.com/kotest/kotest/commit/65263415b83087f34b938d7b462139d8c8b02bc5#diff-679342fbe030cf57a6512c2c94d[…]cd0b688d341b9fa84b14a496dcec7c4e
Shervin
09/03/2025, 7:22 AMOliver.O
09/03/2025, 9:08 AMInstancePerLeaf
case.
The general principle is: If your test needs a fresh context for each leaf or test container, create the context on the required level (in my case, it was the leaf) and create an extension function that "injects" the context into your test case (suspend Context.() -> Unit
). In my above case, I had created a new variant for the test
function that does so.
Having said that, I'm not aware of a clean migration solution that would work in all cases. I always found the Kotest mechanism to re-instantiate specs too complicated to use safely, so I never did.
Maybe something like this work might for you:
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.core.spec.style.scopes.BehaviorSpecWhenContainerScope
import io.kotest.matchers.shouldBe
class MyBehaviorSuite : BehaviorSpec() {
private class MyClass(var myProperty: Int = 1)
private class MyContext {
val myObject: MyClass = MyClass()
}
init {
given("something") {
`when`("it matters") {
then("one", MyContext()) {
myObject.myProperty shouldBe 1
myObject.myProperty += 10
myObject.myProperty shouldBe 11
}
then("two", MyContext()) {
myObject.myProperty shouldBe 1
myObject.myProperty += 20
myObject.myProperty shouldBe 21
}
}
}
}
}
suspend fun <Context> BehaviorSpecWhenContainerScope.then(
name: String,
context: Context,
action: suspend Context.() -> Unit
) = then(name) {
context.action()
}
huehnerlady
09/03/2025, 1:30 PMit
), and not have to think about what I need to do in order to achieve the instance per it
...
I appreciate that you might want to remove the current instancePerLeaf/Test, but maybe there could be a new isolationMode that covers the complete isolation of tests, so people do not have to deal with test bleeding (which is the case at the moment)
The context solution works, my solution works too, but it does make the tests more complex and less readable than the InstancePerLeaf
does/didOliver.O
09/03/2025, 1:43 PMhuehnerlady
09/03/2025, 1:51 PMOliver.O
09/03/2025, 2:25 PMOliver.O
09/05/2025, 6:54 PM