Skip to content

🧠 从 Leetcode 到 Android:测试驱动开发(TDD)在业务中的落地思考

“写测试不是浪费时间,而是为了验证我们对业务的理解是否正确。”


一、为什么聊 TDD

在很多 Android 项目中,测试往往是开发完成之后的事。 但随着业务复杂度提升、团队协作增多,仅靠“手动点点看”已经无法保证逻辑的稳定性。

测试驱动开发(TDD, Test-Driven Development)是一种“先写测试,再写实现”的开发方式。 它要求开发者在动手实现功能之前,就要先明确预期的输入、输出和状态变化。

但 TDD 在 Android 场景下的落地并不容易—— UI 层行为复杂、状态多、输入不固定。 因此我们需要换一个角度来看它。


二、用 Leetcode 理解 TDD 的本质

我们先从算法世界看一个经典题目:

Leetcode 105:从前序与中序遍历序列构造二叉树。

java
/**
 * 给定两个整数数组 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 快速生成多组测试样本。 示例输入(简化版)如下:

java
// 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()

kotlin
class SolutionRepo {
    fun buildTree(preorder: IntArray, inorder: IntArray): TreeNode? {
        return null
    }
}

初始阶段,它返回 null,即所有测试都会失败。


Step 2️⃣:编写测试用例

kotlin
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 还涉及:

  • 界面状态;
  • 用户交互;
  • 生命周期和异步行为。

大多数团队的做法是: 在逻辑层(如 ViewModelUseCase)应用 TDD, 将 UI 行为抽象成状态流(StateFlow / LiveData), 然后针对状态变化进行断言。

重点不是测 UI,而是测业务逻辑。


六、TDD 的真正意义

TDD 并不是“多写几个测试”或“追求 100% 覆盖率”, 而是一个验证理解的过程

它迫使我们在编写实现前,先明确“正确的行为”是什么。

在团队协作中,这种思维方式的价值远大于测试本身。 它让:

  • 产品经理 → 明确状态与结果;
  • 开发者 → 先验证逻辑正确性;
  • 测试团队 → 更聚焦边界与异常。

当每个人都在同一个“状态走向图”上思考时, 整个产品开发链条才真正达成共识。


七、结语

Leetcode 给我们的是确定性问题; 而 Android 给我们的是复杂性问题。

TDD 并不能解决一切,但它提供了一种方式—— 让我们从混乱中抽象出秩序。

“先定义对的是什么,再去实现它。” 这就是测试驱动开发在业务世界中的真正意义。

随便写写的,喜欢就好。 使用VitePress构建