Skip to content
Testing
Skill

/kotest

Write, review, migrate, and debug Kotlin tests with Kotest 6.x: spec styles (FunSpec, DescribeSpec, BehaviorSpec, StringSpec, ...), matchers (shouldBe, shouldThrow, assertSoftly, eventually), data-driven tests (withTests/withData), property-based testing (Arb, checkAll, forAll,

From plugin
kotest-skills
4.8k1 skill

Claude Code

$ npx -y skills add kotest/kotest --skill kotest --agent claude-code

SKILL.md

kotest.SKILL.md
name: kotest
description: >
  Write, review, migrate, and debug Kotlin tests with Kotest 6.x: spec styles (FunSpec, DescribeSpec,
  BehaviorSpec, StringSpec, ...), matchers (shouldBe, shouldThrow, assertSoftly, eventually), data-driven
  tests (withTests/withData), property-based testing (Arb, checkAll, forAll, permutations), lifecycle hooks,
  extensions (Spring, Testcontainers, Koin), ProjectConfig, coroutine/virtual-time testing, tags, and
  Kotlin Multiplatform setup. Use when a project depends on io.kotest, when editing *Spec/*Test.kt files that
  import io.kotest, when the user mentions Kotest, kotest matchers, Arb or property testing in Kotlin, or
  when migrating from JUnit 4/5, TestNG, or Spek to Kotest.
license: Apache-2.0
metadata:
  author: Kotest Team

Kotest

This skill covers Kotest **6.2**. Kotest has three parts that can be used independently: the test framework, the assertions library (which works under any framework, including JUnit), and property testing.

Step 0: Read the Project Before Writing Anything

1. **Find the Kotest version** in `build.gradle.kts` or `gradle/libs.versions.toml`.

  • 6.x needs Kotlin 2.2+ and JDK 11+.
  • On **5.x** many APIs below differ, so check the project's existing tests before using them.

2. **Find the targets**: JVM-only, Android, or KMP (`kotlin("multiplatform")`, `commonTest`). 3. **Match the existing tests**: same spec style, package (`io.kotest` or `com.sksamuel.kotest` in old code), indentation, and helpers. Don't introduce a second spec style into a module. 4. Look for an existing `ProjectConfig` / `PackageConfig` and any registered extensions.

With Bash available, `scripts/analyze-project.sh [dir]` prints a summary of all of this.

Pick the Path

| Task | Read | |---------------------------------------------------------|------------------------------------------------------------------------------| | New project / add Kotest to a build | [SETUP-REFERENCE.md](references/SETUP-REFERENCE.md) | | Write tests in a style | [SPEC-STYLES.md](references/SPEC-STYLES.md) | | Choose a matcher, custom matchers, soft assertions | [ASSERTIONS-REFERENCE.md](references/ASSERTIONS-REFERENCE.md) | | Data-driven tests, config, coroutines, tags, extensions | [FRAMEWORK-REFERENCE.md](references/FRAMEWORK-REFERENCE.md) | | Property-based testing | [PROPERTY-TESTING-REFERENCE.md](references/PROPERTY-TESTING-REFERENCE.md) | | Migrate from JUnit / TestNG / Spek | [MIGRATION-GUIDE.md](references/MIGRATION-GUIDE.md) | | Tests not running, config ignored, hangs | [KOTEST-DEBUGGING.md](references/KOTEST-DEBUGGING.md), [KNOWN-ISSUES.md](references/KNOWN-ISSUES.md) |

Load only the reference you need.

Minimal Setup

// JVM — build.gradle.kts
dependencies {
   testImplementation("io.kotest:kotest-runner-junit5:<version>")
   testImplementation("io.kotest:kotest-assertions-core:<version>")
   testImplementation("io.kotest:kotest-property:<version>")   // if needed
}
tasks.withType<Test>().configureEach { useJUnitPlatform() }

For KMP, apply `id("com.google.devtools.ksp")` and `id("io.kotest")`, then put `kotest-framework-engine` and `kotest-assertions-core` in `commonTest`. Add `kotest-runner-junit5` and `useJUnitPlatform()` for the JVM target. See SETUP-REFERENCE for JS, Wasm, Native, and Android.

Writing Good Kotest Tests

import io.kotest.assertions.assertSoftly
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.assertions.withClue
import io.kotest.core.spec.style.FunSpec
import io.kotest.datatest.withTests
import io.kotest.matchers.collections.shouldContainExactly
import io.kotest.matchers.shouldBe
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.types.shouldBeInstanceOf

class OrderServiceTest : FunSpec({
   val repo = InMemoryOrderRepo()
   val service = OrderService(repo)

   beforeEach { repo.clear() }

   context("placing an order") {
      test("stores the order with its lines") {
         val order = service.place(cart("apple", "pear"))
         assertSoftly(repo.find(order.id)) {
            status shouldBe Status.PLACED
            lines.map { it.sku } shouldContainExactly listOf("apple", "pear")
         }
      }

      test("rejects an empty cart") {
         val e = shouldThrow<IllegalArgumentException> { service.place(cart()) }
         e.message shouldContain "empty"
      }

      withTests(-1, 0) { qty ->
         shouldThrow<IllegalArgumentException> { service.place(cart("apple" to qty)) }
      }
   }

   test("returns a typed result") {
      val result = service.lookup("A-1").shouldBeInstanceOf<Found>()   // smart-casts and returns Found
      withClue("lookup A-1") { result.order.id shouldBe "A-1" }
   }
})

Rules:

  • **Use the most specific matcher.** Write `list.shouldContain(x)`, `str.shouldStartWith("a")`, `n shouldBeGreaterThan 0`

rather than `(cond) shouldBe true`, so the failure message carries the actual values.

  • **Use the returned value from the matcher.** `shouldThrow`, `shouldNotBeNull`, `shouldBeInstanceOf`,

`shouldBeSingle`, and `shouldBeSuccess` return their subject, so you don't need `!!` or casts.

  • **Test bodies are `suspend`.** Never wrap them in `runBlocking`. For delays, use `coroutineTestScope = true` and

virtual time.

  • **Wait with `eventually`, not `Thread.sleep`.** Write `eventually(5.seconds) { ... }` to poll for an asynchronous

result, and `continually` to assert that something stays true.

  • **Group related assertions with `assertSoftly`** so one run reports every failure. Use `withClue` or `asClue` to add context.
  • **Use data-driven tests in
Read more
Stats
4,790
Stars
706
Forks
Active
Maintenance
Kotlin
Language
Apache-2.0
License
Last commit
Created
Added

Repo: kotest/kotest

@kotest
Ships withkotest-skills

Powerful, elegant and flexible test framework for Kotlin with assertions, property testing and data driven tests.

Stars on GitHub

Momentum

stars in total
4.8k
Star history of kotest/kotest on GitHub, a chart by star-history.com

More skills from other plugins.

api-review

api-review

Produce the "Playwright 1.xx API Review" artifact for a range of commits - the public API additions and the breaking or behavior…

@microsoft@microsoftInstall Skill
add-e2e-selectors

add-e2e-selectors

Add reliable @grafana/e2e-selectors to interactive elements and key containers in the Grafana frontend. Use when adding e2e…

grafana5d78k
@grafana@grafanaInstall Skill
promptfoo-evals

promptfoo-evals

Write, refine, run, and QA promptfoo evaluation suites: promptfooconfig.yaml, prompts, providers, vars, tests, assertions,…

promptfoo13d27k
@promptfoo@promptfooInstall Skill
deepeval

deepeval

DeepEval evaluation workflow for AI agents and LLM applications. TRIGGER when the user wants to evaluate or improve an AI agent,…

@confident-ai@confident-aiInstall Skill

Questions about kotest.

Which coding agents can use kotest?
kotest installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.