Kotlin 测试¶
Kotlin 可以测试 Kotlin 代码,也可以测试 Java 代码。由于 Kotlin 与 Java 高度互操作,JUnit、Gradle、Maven、Mockito、AssertJ 等 Java 测试生态大多可以继续使用。同时,Kotlin 标准测试库 kotlin.test 提供了跨平台测试抽象。
JVM 测试基本配置¶
Gradle Kotlin DSL:
plugins {
kotlin("jvm") version "2.4.0"
}
kotlin {
jvmToolchain(17)
}
dependencies {
testImplementation(kotlin("test"))
}
tasks.test {
useJUnitPlatform()
}
kotlin("test") 提供 Kotlin 测试工具,并能与 JUnit 集成。JVM 项目通常使用 JUnit Platform。
使用 JUnit¶
import kotlin.test.Test
import kotlin.test.assertEquals
class CalculatorTest {
@Test
fun addsNumbers() {
assertEquals(4, 2 + 2)
}
}
也可以直接使用 JUnit Jupiter:
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
class CalculatorTest {
@Test
fun addsNumbers() {
assertEquals(4, 2 + 2)
}
}
kotlin.test 的好处是更容易迁移到多平台测试;JUnit Jupiter 的好处是 JVM 生态能力完整。
用 Kotlin 测 Java 代码¶
Java:
public class TodoRepository {
public List<TodoItem> getAll() {
return List.of();
}
}
Kotlin 测试:
class TodoRepositoryTest {
private lateinit var repository: TodoRepository
@BeforeEach
fun setUp() {
repository = TodoRepository()
}
@Test
fun startsEmpty() {
assertTrue(repository.all.isEmpty())
}
}
注意 Kotlin 调 Java getter 时可以用属性语法:repository.all 实际调用 getAll()。
lateinit 在测试中的使用¶
测试中常见:
private lateinit var service: UserService
@BeforeEach
fun setUp() {
service = UserService()
}
这样可以避免把测试 fixture 声明为可空类型:
private var service: UserService? = null
但 lateinit 访问前必须初始化,否则会抛 UninitializedPropertyAccessException。
断言异常¶
Kotlin test:
assertFailsWith<IllegalArgumentException> {
User("")
}
JUnit Jupiter:
Assertions.assertThrows(IllegalArgumentException::class.java) {
User("")
}
在 Java API 需要 Class<T> 时,用 ::class.java。
测试命名¶
Kotlin 允许用反引号写可读测试名:
@Test
fun `user name must not be blank`() {
assertFailsWith<IllegalArgumentException> {
User("")
}
}
这种写法适合测试,但不建议滥用在生产 API 中。部分 Java 工具、老旧测试报告或命令行过滤可能对空格方法名支持不佳。
多平台测试¶
KMP 项目中通常有:
src/commonTest/kotlin
src/jvmTest/kotlin
src/jsTest/kotlin
src/iosTest/kotlin
公共业务逻辑放 commonTest,平台相关行为放具体平台测试。
kotlin {
sourceSets {
commonTest.dependencies {
implementation(kotlin("test"))
}
}
}
不要只跑 JVM 测试就认为多平台逻辑已验证。JS、Native、iOS 的运行时差异可能影响行为。
Mock 与 final 类¶
Kotlin 类默认 final。某些 Java Mock 框架默认依赖继承或代理,可能需要额外配置。
选择:
- 使用支持 Kotlin final/mock 的框架配置。
- 对接口编程。
- 把复杂外部依赖抽象成接口。
- 避免为了测试随意把所有类改成
open。
Java Optional 与 Kotlin 可空测试¶
测试 Java API 时可能遇到 Optional:
val found = repository.getById(id)
assertTrue(found.isPresent)
assertEquals(item, found.get())
Kotlin 自己的 API 通常更倾向返回可空类型:
val found: TodoItem? = repository.findById(id)
assertNotNull(found)
不要在 Kotlin API 中机械使用 Java Optional。
实践建议¶
- JVM 项目优先用 JUnit Platform。
- 多平台库优先使用
kotlin.test。 - 测试 fixture 可以用
lateinit,但只限清晰初始化流程。 - Kotlin 测 Java 代码时注意平台类型和 Java getter 属性映射。
- 不要为了 Mock 破坏生产代码设计,优先抽接口和依赖注入。
- CI 中明确运行所有目标的测试任务。
参考¶
- 官方 Kotlin + JUnit 教程:https://kotlinlang.org/docs/jvm-test-using-junit.html