🧠 从 Leetcode 到 Android:测试驱动开发(TDD)在业务中的落地思考
“写测试不是浪费时间,而是为了验证我们对业务的理解是否正确。”
一、为什么聊 TDD
在很多 Android 项目中,测试往往是开发完成之后的事。 但随着业务复杂度提升、团队协作增多,仅靠“手动点点看”已经无法保证逻辑的稳定性。
测试驱动开发(TDD, Test-Driven Development)是一种“先写测试,再写实现”的开发方式。 它要求开发者在动手实现功能之前,就要先明确预期的输入、输出和状态变化。
但 TDD 在 Android 场景下的落地并不容易—— UI 层行为复杂、状态多、输入不固定。 因此我们需要换一个角度来看它。
二、用 Leetcode 理解 TDD 的本质
我们先从算法世界看一个经典题目:
Leetcode 105:从前序与中序遍历序列构造二叉树。
/**
* 给定两个整数数组 preorder 和 inorder,
* 其中 preorder 是二叉树的先序遍历,inorder 是同一棵树的中序遍历,
* 请构造二叉树并返回其根节点。
*
* - 输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
* - 输出: [3,9,20,null,null,15,7]
*/
在这个题目中,输入(preorder, inorder)与输出(树结构) 之间的关系非常明确。 而每组输入输出对,实际上就是一个“测试用例”。
只要所有测试用例都通过,我们就能确定实现逻辑完全正确。 这正是 TDD 的核心思想。
三、构建测试样本:ChatGPT 的辅助
像算法题这种问题,我们可以借助 ChatGPT 快速生成多组测试样本。 示例输入(简化版)如下:
// 1. 空树
preorder = []
inorder = []
// 输出: []
// 2. 单节点
preorder = [1]
inorder = [1]
// 输出: [1]
// 3. 左子树
preorder = [3,2,1]
inorder = [1,2,3]
// 输出: [3,2,null,1]
// 4. 右子树
preorder = [1,2,3]
inorder = [1,2,3]
// 输出: [1,null,2,null,3]
ChatGPT 可以一次性生成几十个边界条件和复杂情况,大大提升测试覆盖率。 这在真实业务中非常有价值: 开发者可以专注于实现逻辑,而不是穷举测试数据。
四、TDD 的落地过程(以 Android 为例)
Step 1️⃣:定义目标
假设我们要在 Algorithm 层实现 buildTree()
:
class SolutionRepo {
fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? {
return null
}
}
初始阶段,它返回 null
,即所有测试都会失败。
Step 2️⃣:编写测试用例
class SolutionRepoTest {
private val solution = SolutionRepo()
@Test
fun testBuildTree() {
// 遍历所有样例
(1..20).forEach { index ->
assertEquals(
expectedTree(index),
solution.buildTree(preorder(index), inorder(index))
)
}
}
}
测试先写好,哪怕暂时全部失败也没关系。 接下来就是“让测试变绿”的过程。
Step 3️⃣:不断实现 → 验证 → 修正
开发者可以在 IDE 中不断运行 testBuildTree()
, 当所有用例都通过时,就代表逻辑满足了所有预期的状态转换。
这就是最小化风险、提升开发信心的 TDD 循环:
红 → 绿 → 重构 编写失败测试 → 让测试通过 → 优化实现。
五、TDD 在 Android 业务中的挑战
🧩 1. 测试用例覆盖不足
算法题的输入输出是确定的,但业务逻辑往往不止“对”或“错”。 用户操作复杂、数据状态多变、外部依赖不可控。 TDD 无法穷尽所有可能,因此测试与产品、测试团队需要密切合作,共同定义状态场景。
⚠️ 否则容易陷入“为了通过测试而写代码”的陷阱。
🧩 2. Android 的 UI 层 TDD 不如 Backend 简单
Backend 的输入输出可以完全用数据描述,而 Android 还涉及:
- 界面状态;
- 用户交互;
- 生命周期和异步行为。
大多数团队的做法是: 在逻辑层(如 ViewModel
或 UseCase
)应用 TDD, 将 UI 行为抽象成状态流(StateFlow / LiveData), 然后针对状态变化进行断言。
✅ 重点不是测 UI,而是测业务逻辑。
六、TDD 的真正意义
TDD 并不是“多写几个测试”或“追求 100% 覆盖率”, 而是一个验证理解的过程。
它迫使我们在编写实现前,先明确“正确的行为”是什么。
在团队协作中,这种思维方式的价值远大于测试本身。 它让:
- 产品经理 → 明确状态与结果;
- 开发者 → 先验证逻辑正确性;
- 测试团队 → 更聚焦边界与异常。
当每个人都在同一个“状态走向图”上思考时, 整个产品开发链条才真正达成共识。
七、结语
Leetcode 给我们的是确定性问题; 而 Android 给我们的是复杂性问题。
TDD 并不能解决一切,但它提供了一种方式—— 让我们从混乱中抽象出秩序。
“先定义对的是什么,再去实现它。” 这就是测试驱动开发在业务世界中的真正意义。