网络宝典
第二套高阶模板 · 更大气的阅读体验

测试驱动开发实例:从写第一个失败测试开始

发布时间:2026-04-12 11:30:43 阅读:1 次

小张刚接手一个用户注册功能,老板说‘先跑通就行’,他唰唰写了二十行代码,一测——注册成功,但邮箱格式错了也能过,密码没校验长度,连空字符串都放行。第二天测试同学提了五个 bug,他边改边挠头:怎么当初就没想着‘它该干啥’?

不是先写代码,是先问‘它得通过什么才算对’

测试驱动开发(TDD)不神秘,就三步循环:红 → 绿 → 重构。红,是写一个会失败的测试;绿,是写刚好能让它通过的最简代码;重构,是让代码更干净,但测试还得全绿。

来个真实小例子:判断闰年

需求很简单:输入年份,返回 true 或 false。我们不急着写判断逻辑,先写测试:

import unittest

class TestLeapYear(unittest.TestCase):
    def test_2000_is_leap_year(self):
        self.assertTrue(is_leap_year(2000))

    def test_1900_is_not_leap_year(self):
        self.assertFalse(is_leap_year(1900))

运行一下——报错:NameError: name 'is_leap_year' is not defined。这正是我们想要的“红”。接着,只写最简实现让它变绿:

def is_leap_year(year):
    return True  # 先硬编码,让第一个测试过

再跑,第一个测试绿了,第二个失败(因为 1900 返回了 True)。那就补逻辑:

def is_leap_year(year):
    if year % 400 == 0:
        return True
    if year % 100 == 0:
        return False
    if year % 4 == 0:
        return True
    return False

两个测试全绿。此时你心里有底:这个函数至少对 2000 和 1900 是靠谱的。

为什么新手容易卡在第一步?

不是不会写测试,是怕“还没想清楚怎么写,先写测试不是瞎写吗?”其实恰恰相反——写测试的过程,就是在把模糊需求掰成可验证的动作。比如“用户登录后跳转首页”,拆出来就是:
① 输入正确账号密码 → 返回 200;
② 输入错误密码 → 返回 401;
③ 登录成功后 session 里有 user_id。

每一条,都是一个 assert。写完这些,你自然知道接口要返回啥、要设哪些字段、边界在哪。

别追求一步到位,先让一个测试亮起来

有次带实习生做订单状态流转,他盯着“待支付→已支付→已发货→已完成”发呆。我说:“别管全流程,先写一个测试:创建订单,默认状态是‘待支付’。”他秒写完,跑通。接着加第二条:“调用 pay() 方法后,状态变成‘已支付’。”再跑,红了——于是他去看 pay() 该干啥,而不是对着空白函数猜。

TDD 不是给代码上枷锁,是给思考装导航。你不需要一开始就写出完美设计,只需要每次只走一小步,且每一步都有测试盯着。