🧩 第十一章 衍生:敏捷开发
📄 一句话概括

TDD 是红→绿→重构三步循环——实测三条测试里有两条是边界(days 为 0、为负);先写实现的话,这两个边界八成会被忘掉。

📝 本节和前面几节的关系

11.9 列了技术侧的 4 项实践,这一节讲第一项:测试驱动开发。

一、三步循环

红:先写一条测试,跑一下,必然失败(因为实现还不存在)。 绿:写最少的代码让它通过,不追求漂亮。 重构:在测试保护下改善结构,随时能退回绿的状态。

二、实测:两条边界是被迫想出来的

三条测试:fee(100, 0)、fee(100, 5)、fee(100, -3)。 后两条其实是在测边界:天数为 0、天数为负。 如果先写实现,写的时候不会想到传负数——这就是 TDD 的核心收益。

三、测试先行的真正作用

它不是【保证质量】这么笼统,它是逼你先想清楚【什么叫做对了】。 写测试就是写验收标准,写不出来的需求,说明还没想清。 所以 TDD 兼有【设计】的作用:测试写不出来的接口,通常设计得不好。

四、它不适合什么

UI 交互细节、探索性的原型、依赖外部系统的集成点——这些先写测试成本太高。 所以实践里常见的是【核心逻辑用 TDD,边角用别的办法】。 别把它当教条:TDD 是工具,不是信仰。

五、动手 Demo:先编写测试代码,一边运行一边开发的测试驱动开发

先写三条测试(含两个边界),再写实现让它通过。 看这几条测试覆盖了几个边界。

def fee(amount, days):
    if days <= 0:
        return 0
    return amount * days // 10

cases = [(100, 0, 0), (100, 5, 50), (100, -3, 0)]
ok = all(fee(a, d) == e for a, d, e in cases)
print(len(cases), ok)   # 3 True

实跑输出:

--------------------------------------------------------------------
先编写测试代码,一边运行一边开发的测试驱动开发
--------------------------------------------------------------------

  TDD 的三步循环:**红(测试失败)→ 绿(让它过)→ 重构(改善)。**实测:

  先写测试(此时必然失败):

  序号     测试
  ----------------------------------
  1        assert fee(100, 0) == 0
  2        assert fee(100, 5) == 50
  3        assert fee(100, -3) == 0

      测试条数                    3 条
      三条测试全过                True
      实现行数                    4 行
      先写测试带来的边界          2 个(days 为 0、为负)
      循环次数                    3 次(红/绿/重构)

  **注意那 2 个边界测试**:days=0 和 days=-3。
  **如果先写实现,这两个边界八成会被忘掉**——因为写的时候不会想到传负数。
  **这就是 TDD 的核心收益:测试先写,会逼你想清边界。**

实测:3 条测试全部通过,其中 2 条是边界(days=0、days=-3)。 先写实现的话,这两个边界八成会漏——写的时候不会想到传负数。 TDD 顺带起设计作用:测试写不出来的接口,通常设计得不好。

30 秒实验

  1. 给你手上的一个函数先写三条测试(含一个边界),再写实现。
  2. 找一个写不出测试的函数,想想是不是接口没设计好。

常见误区

  1. 以为 TDD 就是先写测试后写代码。核心是【先想清什么叫做对了】。
  2. 以为所有代码都该 TDD。UI、原型、集成点先写测试成本太高。
自测三问(点开对答案) 1. TDD 的三步循环是什么? —— 红(测试失败)、绿(让它过)、重构(改善结构)。 2. 实测三条测试有什么特点? —— 其中 2 条是边界:天数为 0 与天数为负。 3. 测试先行真正的收益? —— 逼你先想清什么叫做对了,兼有设计作用;写不出测试的接口通常设计不好。

相关笔记