
关于
Kotlin 测试模式,涵盖 Kotest、MockK、协程测试、基于属性的测试和 Kover 覆盖率。遵循 TDD 方法论和惯用 Kotlin 实践。
name: kotlin-testing description: Kotlin 测试模式,包含 Kotest、MockK、协程测试、基于属性的测试和 Kover 覆盖率。遵循 TDD 方法论和惯用 Kotlin 实践。 origin: ECC
Kotlin 测试模式
全面的 Kotlin 测试模式,用于编写可靠、可维护的测试,遵循 TDD 方法论,使用 Kotest 和 MockK。
何时使用
- 编写新的 Kotlin 函数或类
- 为现有 Kotlin 代码添加测试覆盖
- 实现基于属性的测试
- 在 Kotlin 项目中遵循 TDD 工作流
- 配置 Kover 进行代码覆盖率
工作原理
- 识别目标代码 — 找到要测试的函数、类或模块
- 编写 Kotest 规范 — 选择匹配测试范围的规范风格(StringSpec、FunSpec、BehaviorSpec)
- 模拟依赖 — 使用 MockK 隔离被测单元
- 运行测试(红色) — 验证测试以预期错误失败
- 实现代码(绿色) — 编写最少代码通过测试
- 重构 — 在保持测试通过的同时改进实现
- 检查覆盖率 — 运行
./gradlew koverHtmlReport并验证 80%+ 覆盖率
示例
以下部分包含每种测试模式的详细可运行示例:
快速参考
- Kotest 规范 — StringSpec、FunSpec、BehaviorSpec、DescribeSpec 示例
- 模拟 — MockK 设置、协程模拟、参数捕获
- TDD 演练 — 使用 EmailValidator 的完整红/绿/重构循环
- 覆盖率 — Kover 配置和命令
- Ktor 测试 — testApplication 设置
Kotlin 的 TDD 工作流
红-绿-重构循环
RED -> 先编写失败的测试
GREEN -> 编写最少代码通过测试
REFACTOR -> 在保持测试通过的同时改进代码
REPEAT -> 继续下一个需求
Kotlin 中的逐步 TDD
// Step 1: Define the interface/signature
// EmailValidator.kt
package com.example.validator
fun validateEmail(email: String): Result<String> {
TODO("not implemented")
}
// Step 2: Write failing test (RED)
// EmailValidatorTest.kt
package com.example.validator
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.result.shouldBeFailure
import io.kotest.matchers.result.shouldBeSuccess
class EmailValidatorTest : StringSpec({
"valid email returns success" {
validateEmail("user@example.com").shouldBeSuccess("user@example.com")
}
"empty email returns failure" {
validateEmail("").shouldBeFailure()
}
"email without @ returns failure" {
validateEmail("userexample.com").shouldBeFailure()
}
})
// Step 3: Run tests - verify FAIL
// $ ./gradlew test
// EmailValidatorTest > valid email returns success FAILED
// Step 4: Implement minimal code (GREEN)
fun validateEmail(email: String): Result<String> {
if (email.isBlank()) return Result.failure(IllegalArgumentException("Email cannot be blank"))
if ('@' !in email) return Result.failure(IllegalArgumentException("Email must contain @"))
val regex = Regex("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")
if (!regex.matches(email)) return Result.failure(IllegalArgumentException("Invalid email format"))
return Result.success(email)
}
Kotest 规范风格
// StringSpec - 最简洁
class MathTest : StringSpec({
"addition works" { (1 + 1) shouldBe 2 }
})
// FunSpec - 类似 JUnit
class MathFunTest : FunSpec({
test("addition works") { (1 + 1) shouldBe 2 }
})
// BehaviorSpec - BDD 风格
class MathBehaviorTest : BehaviorSpec({
given("two numbers") {
`when`("added together") {
then("returns the sum") { (1 + 1) shouldBe 2 }
}
}
})
MockK
import io.mockk.*
// 基本模拟
val repository = mockk<UserRepository>()
every { repository.findById(1) } returns User(1, "Alice")
// 协程模拟
coEvery { repository.findByIdAsync(1) } returns User(1, "Alice")
// 验证
verify(exactly = 1) { repository.findById(1) }
coVerify { repository.findByIdAsync(any()) }
Kover 覆盖率
// build.gradle.kts
plugins {
id("org.jetbrains.kotlinx.kover") version "0.8.3"
}
// 运行覆盖率报告
// ./gradlew koverHtmlReport
// ./gradlew koverVerify
最佳实践
- 每个测试只测试一个行为
- 使用描述性测试名称说明预期行为
- 遵循 Arrange-Act-Assert 模式
- 优先使用
shouldBe等 Kotest 匹配器而非 JUnit 断言 - 对异步代码使用
runTest协程测试
兼容工具
Claude CodeCursor
标签
测试

