引言:为什么 Kotlin 测试是超能力,而不是无聊的义务
\n\n想象一下:你用 Kotlin 写了一个应用程序。它运行正常。你很开心。但一周过去了,你添加了一个新功能——结果旧功能突然坏了。熟悉吗?如果没有——要么你是天才,要么你从未写过没有测试的代码。Kotlin 测试不仅仅是开发者清单上的一个勾选标记。它是你抵御 bug 的个人“盾牌”,是部署后能安心睡觉的方法,也是让代码更清晰、更易理解的工具。
\n\n在本文中,我将向初学者展示如何开始 Kotlin 测试。我们将分析真实示例、流行库以及能为你节省数小时调试时间的技巧。你将学习如何编写单元测试、模拟依赖项以及测试协程。我们开始吧!
\n\n步骤 1:环境配置——前 5 分钟
\n\n在编写第一个测试之前,需要添加依赖项。如果你使用 Gradle(很可能你正在使用),请在 build.gradle.kts 中添加以下行:
// build.gradle.kts\ndependencies {\n // JUnit 5 — 单元测试的基础\n testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")\n \n // MockK — Kotlin 中最好的模拟库\n testImplementation("io.mockk:mockk:1.13.8")\n \n // AssertJ — 漂亮且可读的断言\n testImplementation("org.assertj:assertj-core:3.24.2")\n \n // 用于测试协程\n testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")\n}\n\ntasks.test {\n useJUnitPlatform()\n}\n\n完成!现在你拥有了编写测试所需的一切。请注意:我们使用的是 JUnit 5(而不是 4)——这是现代标准,与 Kotlin 配合得很好。MockK 并非随意选择:它理解 null-safety、扩展函数以及其他 Mockito 难以处理的 Kotlin 特性。
\n\n步骤 2:你的第一个 Kotlin 单元测试
\n\n让我们编写一个简单的类并对其进行测试。假设我们有一个折扣计算器:
\n\n// DiscountCalculator.kt\nclass DiscountCalculator {\n fun calculateDiscount(price: Double, discountPercent: Int): Double {\n require(price >= 0) { "价格不能为负数" }\n require(discountPercent in 0..100) { "折扣必须在 0 到 100 之间" }\n return price * (100 - discountPercent) / 100.0\n }\n}\n\n现在编写测试。在 src/test/kotlin 文件夹中创建文件 DiscountCalculatorTest.kt:
import org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\nimport org.assertj.core.api.Assertions.assertThat\n\nclass DiscountCalculatorTest {\n \n private val calculator = DiscountCalculator()\n \n @Test\n fun `应该正确计算 20% 折扣`() {\n val result = calculator.calculateDiscount(1000.0, 20)\n assertThat(result).isEqualTo(800.0)\n }\n \n @Test\n fun `价格负数时应抛出异常`() {\n assertThrows<IllegalArgumentException> {\n calculator.calculateDiscount(-100.0, 10)\n }\n }\n \n @Test\n fun `折扣为 0% 时应返回原价`() {\n val result = calculator.calculateDiscount(500.0, 0)\n assertThat(result).isEqualTo(500.0)\n }\n}\n\n注意风格:我们使用反引号来编写中文测试名称(Kotlin 允许这样做)。这使得测试即使对非技术同事也易于阅读。AssertJ 提供了流畅的接口:assertThat(result).isEqualTo(800.0) —— 读起来像一个英文句子。
步骤 3:使用 MockK 模拟——测试依赖项时
\n\n在实际应用中,类很少独立存在。它们会访问数据库、API 或其他服务。为了不依赖外部系统,我们使用模拟。假设我们有一个服务