Kotest Conditional Evaluation
Config
Kotest supports disabling tests by setting configuration flags on tests. These configuration flags include enabled, enabledIf, and enabledOrReasonIf.
enabled flags
You can disable a test case by setting the enabled parameter of the config function to false. Conversely, set it to true to enable the test.
The following example shows how to set the enabled parameter:
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
class ConfigEnabledTest : StringSpec({
"always runs".config(enabled = true) {
// This test always runs.
1 + 1 shouldBe 2
}
"never runs".config(enabled = false) {
// This test never runs.
1 + 1 shouldBe 3
}
})
When set to false, the test is not executed. When set to true, the test is executed.
The same mechanism can be used to run tests only under specific conditions. For example, you can use SystemUtils.IS_OS_LINUX from Apache Commons Lang to run a specific test only in a Linux environment.
"runs only on Linux".config(enabled = IS_OS_LINUX) {
// This test runs only on Linux.
}
"runs only on macOS".config(enabled = IS_OS_MAC) {
// This test runs only on macOS.
}
Enabled if
If you want to use a function that is evaluated every time a test is invoked, you can use enabledIf. This function has the form (TestCase) -> Boolean, and it can access the test when evaluating whether the test should be enabled or disabled at runtime.
For example, if you want to disable all tests that start with the word danger unless the environment is Linux, you can write the following:
import io.kotest.core.spec.style.StringSpec
import io.kotest.core.test.EnabledIf
import org.apache.commons.lang3.SystemUtils.IS_OS_LINUX
class ConfigEnabledIf : StringSpec({
val disableDangerOnNotLinux: EnabledIf = { !it.name.testName.startsWith("danger") || IS_OS_LINUX }
"danger will robinson".config(enabledIf = disableDangerOnNotLinux) {
// test here
}
"very safe will".config(enabledIf = disableDangerOnNotLinux) {
// test here
}
})
Enabled or Reason If
The third variant of the enabled flag is enabledOrReasonIf, which can return the reason why a test is disabled. This variant has the form (TestCase) -> Enabled, where Enabled is a type that can include a skipped reason. The reason string is passed to the test report.
For example, the previous danger example can be rewritten as follows:
import io.kotest.core.spec.style.StringSpec
import io.kotest.core.test.Enabled
import io.kotest.core.test.TestCase
import org.apache.commons.lang3.SystemUtils
class ConfigEnabledOrReasonIfTest : StringSpec({
val disableDangerOnFridays: (TestCase) -> Enabled = {
if (it.name.testName.startsWith("danger") || SystemUtils.IS_OS_LINUX)
Enabled.disabled("It's a linux, and we don't like danger!")
else
Enabled.enabled
}
"danger Will Robinson".config(enabledOrReasonIf = disableDangerOnFridays) {
// test here
}
"safe Will Robinson".config(enabledOrReasonIf = disableDangerOnFridays) {
// test here
}
})
The following result is displayed in the console:
Process finished with exit code 0
It's a linux, and we don't like danger!
Focus
In Kotest, Focus is a feature that gives priority to specific tests when running them. Focus lets you run tests with attention on a specific test or test group. This helps developers concentrate on a specific area and debug or develop more efficiently.
Setting Focus is simple. Prefix a test function or test group with f:. The matching test or test group is then focused, which means only that test and all child tests defined inside its scope are executed, and the remaining tests are not executed.
For example, the following code uses Focus:
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
class FocusTest: StringSpec({
"this test is not focused" {
// This test is not executed.
1 + 1 shouldBe 2
}
// This test is focused and executed.
"f: Focus example" {
1 + 1 shouldBe 2
}
"this test is also not focused" {
// This test is not executed.
1 + 1 shouldBe 2
}
})
In this code, only the "f: Focus example" test is focused and executed. The remaining tests are not executed. This helps developers concentrate only on the important area and work more efficiently.
Focus mode does not work for nested tests because nested tests are discovered only after the parent test has executed.
Bang
In Kotest, Bang (!) is used to mark a test so it is ignored and not executed. This is useful when a specific test should not run in the current situation. It is especially useful for temporarily disabling a test during development. You can re-enable the test later without deleting it entirely.
Bang is used before a test function or test group.
For example, the following code uses Bang:
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
class BangTest : StringSpec({
"!this test does not run" {
// This test is not executed.
1 + 1 shouldBe 2
}
"this test runs" {
// This test is executed.
1 + 1 shouldBe 2
}
})
In this code, the test prefixed with !, "!this test does not run", is not executed. The other tests are executed.
You can use Bang this way to temporarily disable a specific test and re-enable it when needed.
X-Method
In Kotest, X-Method is metadata used to disable tests. If you prefix a specific test function or test group with x, that test is not executed. This lets you temporarily disable a specific test and re-enable it later.
For example, the following code uses X-Method:
import io.kotest.core.spec.style.DescribeSpec
class XMethodTest : DescribeSpec({
xdescribe("this block and its children are now disabled") {
it("this test does not run") {
// disabled test
}
}
})
In this code, X-Method is used to disable a specific test group and test. xdescribe represents a disabled group and its tests. The remaining tests are executed.
You can use X-Method this way to temporarily disable a specific test or test group and re-enable it later when needed.
@Ignored
In Kotest, the @Ignored annotation is used to disable all test functions. When this annotation is used, the test functions are not executed. This lets you temporarily disable specific tests and re-enable them later.
For example, the following code uses the @Ignored annotation:
package com.devkuma.conditional.evaluation
import io.kotest.core.annotation.Ignored
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
@Ignored
class IgnoredTest : StringSpec({
"this test does not run" {
// This test is not executed.
1 + 1 shouldBe 3
}
}) {
init {
error("boom") // This error does not occur because the spec is not created.
}
}
In this code, the @Ignored annotation disables all tests.
You can use this annotation to temporarily disable a specific test function and re-enable it when needed.
@EnabledIf
In Kotest, the @EnabledIf annotation is used to enable test functions only when a specific condition is true. This annotation lets you execute a test only when a specific condition is satisfied.
To use the @EnabledIf annotation, you need to create a class that implements the EnabledCondition interface. This interface includes the isEnabled method, and that method determines a specific condition and returns true or false.
For example, the following code uses the @EnabledIf annotation to enable a test based on a specific condition:
package com.devkuma.conditional.evaluation
import io.kotest.core.annotation.EnabledCondition
import io.kotest.core.annotation.EnabledIf
import io.kotest.core.spec.Spec
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
import kotlin.reflect.KClass
@EnabledIf(MyEnabledCondition::class)
class EnabledIfTest2 : StringSpec({
"this test runs when a specific condition is true" {
// This test runs when the condition specified by MyEnabledCondition is true.
1 + 1 shouldBe 2
}
})
object MyEnabledCondition : EnabledCondition {
override fun enabled(kclass: KClass<out Spec>): Boolean {
// Determine the specific condition here and return true or false.
return true // Return true to enable the test when the condition is satisfied.
}
}
In this code, the MyEnabledCondition object implements the EnabledCondition interface. The enabled method determines a specific condition and returns true or false. The @EnabledIf annotation is used to specify this condition. Therefore, the test runs only when the specific condition is true.
This lets you conditionally enable tests based on specific conditions and flexibly control when tests run.