跳转至

测试驱动开发(TDD)

什么是 TDD

测试驱动开发(Test-Driven Development,TDD)是一种先写测试、再写实现的开发方法。

传统开发流程是这样的:

  1. 写功能代码
  2. (如果还有时间的话)补测试
  3. 希望代码是对的

TDD 的流程反过来了:

  1. 先写一个测试,描述你期望代码做什么
  2. 写最少的代码让测试通过
  3. 在测试保护下重构优化

注意,TDD 不是一种测试技术,而是一种开发方法。测试只是手段,真正的目的是帮你写出更好的代码。

TDD 的核心原则

红-绿-重构

TDD 的核心循环只有三步,不断重复:

红(Red) — 写一个失败的测试

你先定义"代码应该做什么"。这个测试必须失败,因为你还没写实现代码。失败证明测试是在工作的,而不是一个永远通过的假测试。

绿(Green) — 写最少的代码让测试通过

目标不是写出完美的代码,而是让测试通过。哪怕实现很丑陋、很硬编码,只要测试通过就行。这一步的关键是"最少" — 别多写。

重构(Refactor) — 在测试保护下优化代码

现在测试通过了,你可以放心地重构:改善命名、消除重复、优化结构。如果重构搞坏了什么,测试会立刻告诉你。

然后回到红,写下一个小功能的测试,如此循环。

测试先行思维

TDD 最有价值的地方不是"有测试",而是先思考"代码应该怎么用"。

当你先写测试时,你在设计 API — 函数叫什么名字、接收什么参数、返回什么结果。这迫使你在写实现之前就想清楚接口,而不是写完代码再去凑一个测试。

好的接口往往是从使用端(调用方)出发设计的,而测试就是最早的"调用方"。

小步迭代

TDD 要求每次只处理一个小需求。不要试图一次写完所有功能 — 写一个测试,通过,再写下一个测试。每次迭代都很小,反馈循环很快。

这让你随时知道代码的状态:要么测试全过,要么刚写的那一步出了问题。出了问题也容易定位,因为改动很小。

TDD 的基本流程

用一个简单的例子来演示。假设我们要写一个函数,判断一个数是否为质数。

第一步:写测试

# test_prime.py
from prime import is_prime

def test_1_is_not_prime():
    assert is_prime(1) is False

运行测试 — 失败了(红)。因为 is_prime 函数还不存在。

第二步:写最少的实现

# prime.py
def is_prime(n):
    return False

运行测试 — 通过了(绿)。没错,最简单的实现就是直接返回 False,因为当前唯一的测试用例就是输入 1。

第三步:添加更多测试

def test_2_is_prime():
    assert is_prime(2) is True

运行测试 — 失败了(红)。因为现在 is_prime 对所有输入都返回 False。

第四步:更新实现

def is_prime(n):
    if n < 2:
        return False
    for i in range(2, n):
        if n % i == 0:
            return False
    return True

运行测试 — 全部通过(绿)。

第五步:重构

实现逻辑已经正确,代码也比较简洁,暂时不需要重构。

继续添加测试:

def test_4_is_not_prime():
    assert is_prime(4) is False

def test_7_is_prime():
    assert is_prime(7) is True

def test_9_is_not_prime():
    assert is_prime(9) is False

全部通过。每加一个测试,你对代码的信心就多一分。

为什么 TDD 对 AI 辅助编程很重要

AI 生成代码的挑战

AI 编程工具(如 Claude、GitHub Copilot)能快速生成代码,但也带来了新的问题:

  • "看起来对"不等于真的对。AI 生成的代码通常语法正确、逻辑通顺,但可能在边界条件下出错。
  • 你可能不完全理解生成的代码。如果看不懂代码,你怎么确定它是对的?
  • 缺乏验证手段时,信心不足。没有测试,你只能手动试几个例子,然后祈祷线上别出事。

这些问题的本质是:AI 大幅降低了写代码的成本,但没有降低验证代码正确性的成本。

TDD 如何解决这些问题

测试即规格。在让 AI 生成代码之前,你先写好测试 — 测试就是你对需求的精确定义。AI 有了测试作为上下文,生成的代码更可能符合你的期望。

自动验证。AI 生成的代码可以直接跑测试。通过了,说明实现符合规格;没通过,说明需要调整。不需要你逐行审查每一行 AI 代码。

安全网。当你需要修改或重构 AI 生成的代码时,测试会告诉你有没有搞坏什么。这在 AI 代码的基础上做二次开发时尤其重要。

渐进式信任。通过一组测试,你可以逐步建立对 AI 代码的信心。测试覆盖得越全面,信心越足。

AI 辅助编程中的 TDD 实践

工作流

在 AI 辅助编程中,TDD 的典型工作流是:

  1. 你用自然语言描述需求
  2. AI 生成测试代码
  3. 你审查测试 — 确保测试覆盖了正确的场景
  4. AI 生成实现代码
  5. 测试自动验证实现是否正确
  6. 你决定是否需要重构

关键点:测试是人和 AI 之间的"契约"。你通过测试告诉 AI 你想要什么,AI 用实现来满足测试。

给 AI 写好提示词

把测试作为提示词的一部分,效果会好很多:

请实现一个 Python 函数 is_prime(n),满足以下测试:

def test_1_is_not_prime():
    assert is_prime(1) is False

def test_2_is_prime():
    assert is_prime(2) is True

def test_4_is_not_prime():
    assert is_prime(4) is False

比起只说"写一个判断质数的函数",提供测试能让 AI 更准确地理解你的需求。

注意事项

测试质量仍然需要人来把关。AI 可能生成"通过测试但逻辑错误"的测试 — 比如测试本身就有 bug,或者测试覆盖的场景不够全面。你不能把测试也完全交给 AI 然后不管了。

边界条件需要人主动考虑。AI 不会自动想到"如果输入是负数怎么办"、"如果输入是 0 呢"。这些边界情况需要你主动添加测试。

不要为了 TDD 而 TDD。如果是一个简单的脚本、一次性的数据处理,不需要严格 TDD。TDD 适合那些需要长期维护、有明确输入输出的代码。

总结

TDD 在 AI 时代的价值比以往更大。AI 让写代码变得廉价,但验证代码正确性仍然是开发者的核心责任。TDD 提供了一种系统化的方法:用确定性的测试来驾驭不确定性的 AI 生成代码。

对于大学学生来说,学习 TDD 不仅是学习一种开发方法,更是培养"先想清楚再动手"的编程习惯。这个习惯在 AI 辅助编程的时代尤为重要 — 因为 AI 能帮你写代码,但只有你能决定代码应该做什么。