kotlin-testing/SKILL.md
Kotlin testing patterns with Kotest, MockK, coroutine testing, property-based testing, and Kover coverage. Follows TDD methodology with idiomatic Kotlin practices.
npx skillsauth add lidge-jun/cli-jaw-skills kotlin-testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Comprehensive Kotlin testing patterns for writing reliable, maintainable tests following TDD methodology with Kotest and MockK.
./gradlew koverHtmlReport and verify 80%+ coverageRED → Write a failing test first
GREEN → Write minimal code to pass the test
REFACTOR → Improve code while keeping tests green
REPEAT → Continue with next requirement
For full step-by-step EmailValidator walkthrough, see references/code-examples.md.
class UserServiceTest : FunSpec({
val repository = mockk<UserRepository>()
val service = UserService(repository)
test("getUser returns user when found") {
val expected = User(id = "1", name = "Alice")
coEvery { repository.findById("1") } returns expected
val result = service.getUser("1")
result shouldBe expected
}
test("getUser throws when not found") {
coEvery { repository.findById("999") } returns null
shouldThrow<UserNotFoundException> {
service.getUser("999")
}
}
})
For StringSpec, BehaviorSpec, and DescribeSpec examples, see references/code-examples.md.
// Equality
result shouldBe expected
result shouldNotBe unexpected
// Strings
name shouldStartWith "Al"
name shouldEndWith "ice"
name shouldContain "lic"
name shouldMatch Regex("[A-Z][a-z]+")
name.shouldBeBlank()
// Collections
list shouldContain "item"
list shouldHaveSize 3
list.shouldBeSorted()
list.shouldContainAll("a", "b", "c")
list.shouldBeEmpty()
// Nulls
result.shouldNotBeNull()
result.shouldBeNull()
// Types
result.shouldBeInstanceOf<User>()
// Numbers
count shouldBeGreaterThan 0
price shouldBeInRange 1.0..100.0
// Exceptions
shouldThrow<IllegalArgumentException> {
validateAge(-1)
}.message shouldBe "Age must be positive"
shouldNotThrow<Exception> {
validateAge(25)
}
For custom matchers, see references/code-examples.md.
class UserServiceTest : FunSpec({
val repository = mockk<UserRepository>()
val logger = mockk<Logger>(relaxed = true) // Returns defaults for all calls
val service = UserService(repository, logger)
beforeTest {
clearMocks(repository, logger)
}
test("findUser delegates to repository") {
val expected = User(id = "1", name = "Alice")
every { repository.findById("1") } returns expected
val result = service.findUser("1")
result shouldBe expected
verify(exactly = 1) { repository.findById("1") }
}
test("suspend function mocking") {
coEvery { repository.findById("1") } returns User(id = "1", name = "Alice")
val result = service.getUser("1")
result.name shouldBe "Alice"
coVerify { repository.findById("1") }
}
})
For advanced MockK patterns (argument capture, spy, coroutine mocking), see references/code-examples.md.
Use runTest from kotlinx.coroutines.test for suspend functions and coroutines:
test("concurrent fetches complete together") {
runTest {
val service = DataService(testScope = this)
val result = service.fetchAllData()
result.users.shouldNotBeEmpty()
}
}
For Flow testing, TestDispatcher, and advanced coroutine patterns, see references/code-examples.md.
Property-based testing: Automatically generates test cases using Kotest's Arb (arbitrary) generators. Great for pure functions.
Data-driven testing: withData allows parameterized test cases for multiple inputs.
Examples and custom generators available in references/code-examples.md.
Use beforeTest/afterTest for test-level setup/cleanup, and beforeSpec/afterSpec for suite-level initialization.
beforeSpec { db = setupDatabase() }
afterSpec { db.close() }
beforeTest { db.clear() }
Kotest extensions enable reusable test infrastructure. See references/code-examples.md for DatabaseExtension example.
// build.gradle.kts
plugins {
id("org.jetbrains.kotlinx.kover") version "0.9.7"
}
kover {
reports {
total {
html { onCheck = true }
xml { onCheck = true }
}
filters {
excludes {
classes("*.generated.*", "*.config.*")
}
}
verify {
rule {
minBound(80) // Fail build below 80% coverage
}
}
}
}
./gradlew koverHtmlReport # Generate HTML report
./gradlew koverVerify # Verify coverage meets thresholds
./gradlew koverXmlReport # Generate XML for CI
# View report (macOS)
open build/reports/kover/html/index.html
| Code Type | Target | |-----------|--------| | Critical business logic | 100% | | Public APIs | 90%+ | | General code | 80%+ | | Generated / config code | Exclude |
./gradlew test # Run all tests
./gradlew test --tests "com.example.UserServiceTest" # Run specific class
./gradlew test --tests "com.example.UserServiceTest.getUser*" # Run by pattern
./gradlew test --info # Verbose output
./gradlew koverHtmlReport # Tests + coverage report
./gradlew detekt # Static analysis
./gradlew ktlintCheck # Formatting check
./gradlew test --continuous # Watch mode
coEvery/coVerify — Never use every/verify for coroutines.runTest for coroutines — Never use Thread.sleep() in coroutine tests; use advanceTimeBy or advanceUntilIdle.data classes — Makes test intent clear and setup reusable.relaxed = true for loggers/observers — They're usually not critical to test assertions.advanceTimeBy or runTest scheduler control instead of delays.references/code-examples.md for StringSpec, BehaviorSpec, DescribeSpecreferences/code-examples.mdreferences/code-examples.mdreferences/code-examples.mdreferences/code-examples.mdreferences/code-examples.mdRemember: Tests are documentation. They show how your Kotlin code is meant to be used. Use Kotest's expressive matchers to make tests readable and MockK for clean mocking of dependencies.
tools
Use only on the Codex CLI for native image generation or image editing without an API key. Save final PNG files under ~/.cli-jaw/uploads, report web-ready absolute-path markdown, and send to Telegram or Discord only when explicitly requested.
tools
Ranked repository structure map via `cli-jaw map`. Use for codebase overview, structure map, symbol overview, unfamiliar codebase exploration, architecture orientation. Triggers: repo map, structure map, codebase overview, 와꾸, project structure, unfamiliar code.
tools
cli-jaw Design workspace: create, preview, run, and export design pages from the right sidebar. Covers panel UX, direct-write workflow, artifact lifecycle, wireframe generation, design system, and Open Design adapter.
development
MUST USE for infrastructure and delivery work — container builds, deploy pipelines, Kubernetes, Infrastructure as Code, SRE foundations, edge/serverless, ML infrastructure. Triggers: Dockerfile, K8s manifests, CI/CD pipeline, Terraform/IaC, release/deploy, devops/infra/deploy or release_cd task_tags.