为什么TDD不是又一种时髦的方法论,而是你新的超级英雄披风
想象一下:你像艺术家一样编写代码——一笔一划,一行一行。你确信一切都非常出色。但过了一个小时,你运行应用程序,它却崩溃了,出现了一个你要找半天的错误。熟悉吗?
现在想象另一个场景。你先编写一个肯定会失败的测试。然后——编写最少的代码让测试通过。只有在那之后——你才进行重构、改进,但此时已经问心无愧。这就是测试驱动开发(TDD),而在Kotlin中,它变成了一种真正的享受。
在本文中,我们不会进行枯燥的讲解。我们将拿一个真实示例,经历TDD的所有三个阶段(Red — Green — Refactor),并展示如何无忧无惧地编写可靠的代码。准备好了吗?出发!
TDD:三个将改变你代码的字母
TDD(测试驱动开发)不仅仅是一种技术,更是一种哲学。核心很简单:先测试,后代码。听起来很疯狂?乍一看——是的。实际上——它能节省数小时的调试时间。
测试驱动开发循环
- Red — 编写一个不通过的测试(它应该失败)。
- Green — 编写最少的代码让测试通过。
- Refactor — 改进代码,不改变其行为。
如此循环,小步前进。每一步都让你确信没有破坏任何东西。在Kotlin中,凭借其简洁的语法和空安全特性,TDD变得更加愉快。
工具集:Kotlin中TDD需要什么
在深入代码之前,让我们准备好武器库。对于Kotlin单元测试,你需要:
- JUnit 5 — 单元测试的事实标准。
- MockK — 专为Kotlin创建的模拟库(不再有null和companion object的问题)。
- Gradle 或 Maven — 用于管理依赖。
在 build.gradle.kts 中添加:
dependencies { testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") testImplementation("io.mockk:mockk:1.13.8")}完成。现在可以编写拿得出手的代码了。
编写第一个TDD循环:有野心的计算器
我们从经典开始——计算器。但不是普通的,而是支持字符串运算的。任务:函数 calculate 接收一个形如 "2 + 3" 的字符串,并返回 5。
步骤1:Red — 编写一个失败的测试
创建测试类:
import org.junit.jupiter.api.Testimport org.junit.jupiter.api.Assertions.assertEquals
class CalculatorTest {
@Test fun `should add two numbers`() { val calculator = StringCalculator() val result = calculator.calculate("2 + 3") assertEquals(5, result) }}运行——测试失败,因为 StringCalculator 类还不存在。红灯亮起——一切按计划进行。
步骤2:Green — 编写最少的代码让测试通过
编写刚好能让测试通过的代码。没有任何架构上的花哨:
class StringCalculator { fun calculate(expression: String): Int { val parts = expression.split(" + ") return parts[0].toInt() + parts[1].toInt() }}现在测试通过了。太好了!但代码目前还很丑陋——它只针对两个数的加法。
步骤3:Refactor — 让代码变得漂亮
进行重构,但不改变行为。添加对不同运算的支持,将逻辑提取到单独的函数中:
class StringCalculator { fun calculate(expression: String): Int { val (a, op, b) =