Kotlin TDD 命令¶
此命令强制采用使用 Kotest、MockK 和 Kover 的 Kotlin 代码测试驱动开发方法论。
此命令的工作方式¶
- 定义类型/接口:先搭建函数签名
- 编写 Kotest 测试:创建全面的测试规格(RED)
- 运行测试:验证测试因正确原因而失败
- 实现代码:编写使测试通过的最小代码(GREEN)
- 重构:在保持测试绿色的同时改进
- 检查覆盖率:使用 Kover 确保 80% 以上的覆盖率
使用时机¶
使用 /kotlin-test 的情况:
- 实现新的 Kotlin 函数或类时
- 为现有 Kotlin 代码添加测试覆盖率时
- 修复 bug 时(先编写失败的测试)
- 构建重要业务逻辑时
- 学习 Kotlin TDD 工作流时
TDD 循环¶
RED -> 失敗するKotestテストを書く
GREEN -> テストを通す最小限のコードを実装
REFACTOR -> コードを改善、テストはグリーンのまま
REPEAT -> 次のテストケースへ
会话示例¶
User: /kotlin-test ユーザー登録リクエストをバリデートする関数が必要です
Agent:
# TDDセッション: 登録バリデーター
## ステップ 1: インターフェース定義
```kotlin
// validator/RegistrationValidator.kt
package com.example.validator
data class RegistrationRequest(
val name: String,
val email: String,
val password: String,
)
sealed class ValidationResult {
data object Valid : ValidationResult()
data class Invalid(val errors: List<String>) : ValidationResult()
}
fun validateRegistration(request: RegistrationRequest): ValidationResult {
TODO("not implemented")
}
```
## ステップ 2: Kotestテストを書く(RED)
```kotlin
// validator/RegistrationValidatorTest.kt
package com.example.validator
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeInstanceOf
class RegistrationValidatorTest : FunSpec({
test("valid registration returns Valid") {
val request = RegistrationRequest(
name = "Alice",
email = "alice@example.com",
password = "SecureP@ss1",
)
val result = validateRegistration(request)
result.shouldBeInstanceOf<ValidationResult.Valid>()
}
test("blank name returns Invalid") {
val request = RegistrationRequest(
name = "",
email = "alice@example.com",
password = "SecureP@ss1",
)
val result = validateRegistration(request)
val invalid = result.shouldBeInstanceOf<ValidationResult.Invalid>()
invalid.errors shouldBe listOf("Name is required")
}
test("invalid email returns Invalid") {
val request = RegistrationRequest(
name = "Alice",
email = "not-an-email",
password = "SecureP@ss1",
)
val result = validateRegistration(request)
val invalid = result.shouldBeInstanceOf<ValidationResult.Invalid>()
invalid.errors shouldBe listOf("Invalid email format")
}
test("short password returns Invalid") {
val request = RegistrationRequest(
name = "Alice",
email = "alice@example.com",
password = "short",
)
val result = validateRegistration(request)
val invalid = result.shouldBeInstanceOf<ValidationResult.Invalid>()
invalid.errors shouldBe listOf("Password must be at least 8 characters")
}
test("multiple errors returns all errors") {
val request = RegistrationRequest(
name = "",
email = "bad",
password = "short",
)
val result = validateRegistration(request)
val invalid = result.shouldBeInstanceOf<ValidationResult.Invalid>()
invalid.errors.size shouldBe 3
}
})
```
## ステップ 3: テスト実行 - FAIL確認
```bash
$ ./gradlew test
RegistrationValidatorTest > valid registration returns Valid FAILED
kotlin.NotImplementedError: An operation is not implemented
FAILED (5 tests, 0 passed, 5 failed)
```
✓ テストが期待通りに失敗(NotImplementedError)。
## ステップ 4: 最小限のコードを実装(GREEN)
```kotlin
// validator/RegistrationValidator.kt
package com.example.validator
private val EMAIL_REGEX = Regex("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$")
private const val MIN_PASSWORD_LENGTH = 8
fun validateRegistration(request: RegistrationRequest): ValidationResult {
val errors = buildList {
if (request.name.isBlank()) add("Name is required")
if (!EMAIL_REGEX.matches(request.email)) add("Invalid email format")
if (request.password.length < MIN_PASSWORD_LENGTH) add("Password must be at least $MIN_PASSWORD_LENGTH characters")
}
return if (errors.isEmpty()) ValidationResult.Valid
else ValidationResult.Invalid(errors)
}
```
## ステップ 5: テスト実行 - PASS確認
```bash
$ ./gradlew test
RegistrationValidatorTest > valid registration returns Valid PASSED
RegistrationValidatorTest > blank name returns Invalid PASSED
RegistrationValidatorTest > invalid email returns Invalid PASSED
RegistrationValidatorTest > short password returns Invalid PASSED
RegistrationValidatorTest > multiple errors returns all errors PASSED
PASSED (5 tests, 5 passed, 0 failed)
```
✓ 全テスト通過!
## ステップ 6: カバレッジチェック
```bash
$ ./gradlew koverHtmlReport
Coverage: 100.0% of statements
```
✓ カバレッジ: 100%
## TDD完了!
测试模式¶
StringSpec(最简单)¶
class CalculatorTest : StringSpec({
"add two positive numbers" {
Calculator.add(2, 3) shouldBe 5
}
})
BehaviorSpec(BDD)¶
class OrderServiceTest : BehaviorSpec({
Given("a valid order") {
When("placed") {
Then("should be confirmed") { /* ... */ }
}
}
})
数据驱动测试¶
class ParserTest : FunSpec({
context("valid inputs") {
withData("2026-01-15", "2026-12-31", "2000-01-01") { input ->
parseDate(input).shouldNotBeNull()
}
}
})
协程测试¶
class AsyncServiceTest : FunSpec({
test("concurrent fetch completes") {
runTest {
val result = service.fetchAll()
result.shouldNotBeEmpty()
}
}
})
覆盖率命令¶
# カバレッジ付きでテスト実行
./gradlew koverHtmlReport
# カバレッジ閾値を検証
./gradlew koverVerify
# CI用XMLレポート
./gradlew koverXmlReport
# HTMLレポートを開く
open build/reports/kover/html/index.html
# 特定のテストクラスを実行
./gradlew test --tests "com.example.UserServiceTest"
# 詳細出力で実行
./gradlew test --info
覆盖率目标¶
| 代码类型 | 目标 |
|---|---|
| 关键业务逻辑 | 100% |
| 公共 API | 90% 以上 |
| 一般代码 | 80% 以上 |
| 生成代码 | 排除 |
TDD 最佳实践¶
应该做:
- 在实现之前先编写测试
- 每次变更后运行测试
- 使用 Kotest 匹配器编写富有表现力的断言
- 对挂起函数使用 MockK 的 coEvery/coVerify
- 测试行为而非实现细节
- 包含边界情况(空、null、最大值)
不应该做:
- 在测试之前编写实现
- 跳过 RED 阶段
- 直接测试私有函数
- 在协程测试中使用 Thread.sleep()
- 忽略不稳定测试
相关命令¶
/kotlin-build— 修复构建错误/kotlin-review— 实现后审查代码verification-loop技能 — 运行完整验证循环
相关¶
- 技能:
skills/kotlin-testing/ - 技能:
skills/tdd-workflow/
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 ev/ECC