什么时候该用测试驱动开发
很多人听说测试驱动开发(TDD)都说好,写代码前先写测试,听起来像在绕远路。但其实它不是万能钥匙,也不是每个项目都得套上。有些场景下,TDD 不仅不累赘,反而能省下大量返工时间。
核心逻辑复杂的模块
比如你正在写一个订单折扣计算引擎,满减、会员折扣、优惠券叠加,规则一多,手动画流程图都容易漏。这时候用 TDD,先把各种组合情况写成测试用例,再动手实现逻辑,每加一条规则都能立刻验证是否影响旧逻辑。
def test_apply_discounts():
order = Order(total=200)
apply_coupon(order, "SUMMER10")
apply_vip_discount(order)
assert order.final_amount == 170
代码还没写完,但测试先列清楚了,等于提前把需求“翻译”成了可执行的条件。开发过程中随时运行,改崩了立马知道。
需要长期维护的系统
公司内部的审批流程系统,用了五年还在加功能。每次改一点,老板就问:“会不会影响之前的?” 如果没有自动化测试,只能靠人工点一遍,效率低还容易漏。
用 TDD 从一开始就建立测试覆盖,新功能先写测试,再实现。久而久之,整个系统就像穿上了防护服。后来的人接手也不怕动哪塌哪。
团队协作频繁的项目
三个人同时开发同一个用户模块,有人改注册逻辑,有人调登录接口。如果没有明确边界和验证手段,合并代码时经常出问题。TDD 提供了一种“契约式开发”模式——你写的测试就是你对功能的承诺。
比如前端同事依赖一个返回用户角色的接口,后端可以先写出测试用例:
def test_get_user_role():
user = create_user(role="admin")
response = client.get(f"/user/{user.id}/role")
assert response.json()["role"] == "admin"
测试通过才算完成,避免了“我以为返回的是字符串”的扯皮。
重构或技术升级前的准备
老系统想从 Flask 迁移到 FastAPI,或者数据库从 MySQL 换成 PostgreSQL。直接动手风险太大。这时候可以先补全测试用例,用 TDD 的方式把现有行为“冻结”下来。
迁移过程中不断运行原有测试,确保功能不变。相当于给重构装了个安全气囊。
不适合 TDD 的情况也得看清
UI 布局调整、原型验证、临时脚本这类变化快、生命周期短的任务,硬套 TDD 反而拖慢节奏。它的优势在于稳定性和可持续性,而不是快速出 demo。
还有那种需求天天变的项目,今天要做支付,明天砍掉,后天又加回来。这种环境下写测试可能比写业务还累。TDD 更适合需求相对清晰、质量要求高的部分。
从小处开始更现实
不用一上来就全项目 TDD。可以挑一个核心服务,比如用户认证模块,试着先写测试再编码。跑通一次就知道它不是形式主义,而是帮你理清思路的工具。
很多开发者试过一次后发现,写着写着,bug 少了,文档也有了(测试本身就是文档),连交接都变得轻松。