Kotest Isolation Modes

Isolation Modes control how the test engine creates spec instances for test cases. In other words, they determine how test instances are created for each test case.

Isolation Modes

Depending on how you configure Isolation Modes, test performance can differ, and you can also prevent test failures caused by instance reuse. For that reason, this is something you should understand clearly.

There are three ways to configure Isolation Mode. It is controlled by the IsolationMode enum, which provides InstancePerTest, SingleInstance, and InstancePerLeaf. Use whichever mode fits your situation.

If you want tests to run in new instances so that state shared between tests can be initialized, change the isolation mode.

You can do this by assigning a value to isolationMode with the following DSL:

class MyTest : WordSpec({
    isolationMode = IsolationMode.SingleInstance
    
    // tests here
})

Or you can override the fun isolationMode(): IsolationMode function:

class MyTest : WordSpec() {
    override fun isolationMode() = IsolationMode.SingleInstance

    init {
        // tests here
    }
}

SingleInstance

SingleInstance, the default isolation mode, creates one instance of the Spec class and then runs each test case in order until all tests are complete.

This mode allows state to be shared between all tests, so it is useful when you want to keep dependencies between tests.

package com.devkuma.kotest.tutorial.isolation

import io.kotest.core.spec.style.WordSpec
import java.util.*

class SingleInstanceExample : WordSpec({

    val id = UUID.randomUUID()

    "a" should {
        println("a: $id")
        "b" {
            println("b: $id")
        }
        "c" {
            println("c: $id")
        }
    }
})

The following is the result of running the example:

a: 3823310d-43d3-47a0-953b-fa3afeb39307
b: 3823310d-43d3-47a0-953b-fa3afeb39307
c: 3823310d-43d3-47a0-953b-fa3afeb39307

The same ID is printed three times because the same instance is used for every test.

InstancePerTest

The next mode, IsolationMode.InstancePerTest, creates a new Spec for every test case, including internal contexts. In other words, the outer context is executed as a standalone test in its own Spec instance.

The example makes this clear.

package com.devkuma.kotest.tutorial.isolation

import io.kotest.core.spec.IsolationMode
import io.kotest.core.spec.style.WordSpec
import java.util.*

class InstancePerTestExample : WordSpec() {

    override fun isolationMode(): IsolationMode = IsolationMode.InstancePerTest

    val id = UUID.randomUUID()

    init {
        "a" should {
            println("a: $id")
            "b" {
                println("b: $id")
            }
            "c" {
                println("c: $id")
            }
        }
    }
}

The following is the result of running the example:

a: 19c3b379-a688-428c-b3e8-8cb507bdf083
a: ffaff5f6-5c6a-41a9-98b9-52f9ca134dfa
b: ffaff5f6-5c6a-41a9-98b9-52f9ca134dfa
a: 08e42544-def7-4859-8b2b-34bb50fe83b0
c: 08e42544-def7-4859-8b2b-34bb50fe83b0

The outer context, test "a", runs first, then runs again for test "b", and again for test "c". This is very useful when you want to reuse variables each time from a clean instance of the Spec class.

InstancePerLeaf

IsolationMode.InstancePerLeaf creates a new instance for every test case, including internal contexts, and runs them independently. This mode prevents state from being shared between test cases and guarantees test independence. However, keep in mind that it consumes more resources.

You can confirm this with the following example.

package com.devkuma.kotest.tutorial.isolation

import io.kotest.core.spec.IsolationMode
import io.kotest.core.spec.style.WordSpec
import java.util.*

class InstancePerLeafExample : WordSpec() {

    override fun isolationMode(): IsolationMode = IsolationMode.InstancePerLeaf

    private val id = UUID.randomUUID()

    init {
        "a" should {
            println("a: $id")
            "b" {
                println("b: $id")
            }
            "c" {
                println("c: $id")
            }
        }
    }
}

The following is the result of running the example:

a: 71830c57-6ee0-4b1f-8c32-812defac2b1e
b: 71830c57-6ee0-4b1f-8c32-812defac2b1e
a: 480ee65a-df4c-4a78-ac80-b6ae2c85ee20
c: 480ee65a-df4c-4a78-ac80-b6ae2c85ee20

Test "a" runs first, and test "b" runs in the same instance. Then a new Spec is created, test "a" runs again, and then test "c" runs.

Global Isolation Mode

You can also configure Isolation Mode globally through a system property.

object GlobalIsolationConfiguration : AbstractProjectConfig() {
    override val isolationMode = IsolationMode.SingleInstance
}

References