Configures and runs Kotest - Kotlin-native test framework with multiple specification styles (StringSpec, FunSpec, BehaviorSpec, DescribeSpec, ShouldSpec, FreeSpec, FeatureSpec, ExpectSpec, AnnotationSpec); rich matcher library; built-in property-based testing (alternative to jqwik); coroutines support; data-driven testing; isolation modes per-spec or per-test; integrates with Gradle JVM test task. Use when working with Kotlin and wanting Kotlin-idiomatic DSL over JUnit 5's annotation-driven approach. Matchers (shouldBe, shouldContain) are bundled with the runner and are not a drop-in replacement for a standalone JVM assertion library; for assertion-only use paired with JUnit 5 / TestNG / Spock see assertj.
80
100%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Per kotest.io/docs:
Kotest is the Kotlin-native test framework. Differentiated from JUnit 5 (which works fine with Kotlin too) by:
shouldBe, shouldContain, shouldThrow, etc.jqwik-testingrunTest/runBlocking integrated cleanlyFor multi-language JVM projects, JUnit 5 is more universal. For Kotlin-only or Kotlin-primary, Kotest's DSL is more ergonomic.
kotest-runner-junit5 + kotest-assertions-core (and kotest-property for PB) and enable useJUnitPlatform() (Step 1).shouldBe matcher (Step 2).checkAll property-based cases and withData data-driven rows where inputs vary (Steps 4, 6).isolationMode when specs carry mutable state (references/matchers-and-isolation.md, Step 7)../gradlew test jacocoTestReport in CI - same runner + JaCoCo as JUnit 5 (Step 8).build.gradle.kts:
dependencies {
testImplementation("io.kotest:kotest-runner-junit5:5.9.1")
testImplementation("io.kotest:kotest-assertions-core:5.9.1")
testImplementation("io.kotest:kotest-property:5.9.1") // for property-based
}
tasks.test {
useJUnitPlatform()
}Per kt-docs Kotest supports 8+ styles. Common picks:
StringSpec (terse, no nesting):
class CalculatorTest : StringSpec({
"adds two numbers" {
Calculator.add(1, 2) shouldBe 3
}
"throws on overflow" {
shouldThrow<ArithmeticException> {
Calculator.add(Int.MAX_VALUE, 1)
}
}
})FunSpec (most familiar to JUnit / pytest users):
class CalculatorTest : FunSpec({
test("adds two numbers") {
Calculator.add(1, 2) shouldBe 3
}
context("overflow handling") {
test("throws on max + 1") {
shouldThrow<ArithmeticException> {
Calculator.add(Int.MAX_VALUE, 1)
}
}
}
})BehaviorSpec (Given/When/Then BDD):
class UserServiceTest : BehaviorSpec({
given("a registered user") {
val user = User("alice@example.com")
`when`("they update their email") {
user.updateEmail("new@example.com")
then("the email is updated") {
user.email shouldBe "new@example.com"
}
}
}
})Pick one style per project + stick with it.
Assert with the rich matcher library - equality, null, string, collection, map, type, Kotlin Result, and shouldThrow<E>. Full catalog in references/matchers-and-isolation.md.
Built-in (no separate library):
class PropertyTest : StringSpec({
"addition is commutative" {
checkAll<Int, Int> { a, b ->
a + b shouldBe b + a
}
}
"concatenation length" {
checkAll(Arb.string(), Arb.string()) { a, b ->
(a + b).length shouldBe a.length + b.length
}
}
})For deeper property-based work see jqwik-testing
or the dedicated qa-property-based plugin.
class AsyncTest : StringSpec({
"fetches user data" {
val user = fetchUserAsync(1) // suspend function
user.id shouldBe 1
}
})Test bodies are suspend functions; runTest etc. wrappers from
kotlinx-coroutines-test work directly.
class DataDrivenTest : FunSpec({
context("addition") {
withData(
Triple(1, 2, 3),
Triple(0, 0, 0),
Triple(-1, 1, 0),
) { (a, b, expected) ->
(a + b) shouldBe expected
}
}
})Each row reports as a separate test - failures don't stop subsequent rows.
Four modes (default SingleInstance); set isolationMode per-spec or globally via AbstractProjectConfig when specs share mutable state. Mode table + example in references/matchers-and-isolation.md.
Same as JUnit 5 (Kotest's runner is kotest-runner-junit5):
- run: ./gradlew test jacocoTestReportJaCoCo coverage works identically.
Testing Calculator.add with FunSpec + property-based checks:
io.kotest:kotest-runner-junit5:5.9.1, kotest-assertions-core:5.9.1, kotest-property:5.9.1; enable useJUnitPlatform().class CalculatorTest : FunSpec({ ... }) with test("adds two numbers") { Calculator.add(1, 2) shouldBe 3 }.test("addition is commutative") { checkAll<Int, Int> { a, b -> a + b shouldBe b + a } }../gradlew test: the example case passes; checkAll generates many pairs and reports the first counterexample if the invariant breaks.jacocoTestReport confirms the add path is covered.| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Mix multiple spec styles in one project | Reader confusion | Pick one (Step 2) |
| Default isolation + shared mutable state | Tests interfere | InstancePerTest mode (Step 7) |
| Use Kotest property-based + jqwik in same project | Two PB libraries | Pick one |
assertEquals(a, b) (JUnit style) | Mixes paradigms | Use a shouldBe b (Step 3) |
shouldContainExactly vs
shouldContainAll).junit5-tests,
spock-tests,
testng-tests,
scalatest - sister toolsjqwik-testing - JVM property-basedtest-code-conventions