测试驱动开发单元测试:先写测试,再写代码的实战套路

小张最近在写一个用户登录验证功能,改了三次逻辑,结果每次上线后都冒出新 bug。同事老李看了眼他的代码,笑着说:"你试试先写测试?"——这可不是开玩笑,而是测试驱动开发(TDD)最朴素的起点。

什么叫测试驱动开发单元测试?

说白了,就是把“写测试”这件事提前到“写功能代码”之前。不是等代码写完再补几个测试用例应付检查,而是先想清楚这个函数该干啥、边界在哪、出错怎么报,然后立刻把它变成一段可运行的失败测试。只有当测试从红变绿(失败→通过),才允许动业务代码。

举个真实例子:加法函数

假设你要写一个 add 函数,支持两个整数相加。别急着敲 return a + b,先写测试:

import unittest

def add(a, b):
    pass  # 先留空,让测试直接失败

class TestAdd(unittest.TestCase):
    def test_add_two_positive_numbers(self):
        self.assertEqual(add(2, 3), 5)

if __name__ == '__main__':
    unittest.main()

运行一下,测试报错:AssertionError: None != 5。好,现在你知道目标了——让 add 返回正确结果。于是补上实现:

def add(a, b):
    return a + b

再跑测试,绿了。接着加边界场景:负数、零、大数溢出(视语言而定)。每加一条测试,都逼自己想得更细一点。

为什么它比“先写代码再补测试”靠谱?

你有没有试过给一段 300 行的老代码写单元测试?往往刚写两行就发现:参数含义模糊、依赖外部状态、函数干了五件事还带副作用……这时候不是在测代码,是在考古。而 TDD 强制你在写第一行业务逻辑前,就定义清晰的输入输出契约。函数自然变得更小、更纯、更易组合。

新手常踩的坑

— 刻意追求 100% 覆盖率,连 if True: 都要 mock;
— 把测试写成业务代码的复刻,比如测试里又算一遍 2+3,等于没测;
— 测试命名糊弄事,比如 test_1(),过两周自己都看不懂在验啥。

记住:好的测试名是句子,比如 test_add_returns_zero_when_both_inputs_are_zero,读一遍就知道它守护哪条逻辑。

工具不挑人,从 Python 的 unittest 开始就行

不用等团队统一框架,也不用装一堆插件。Python 自带 unittest,Java 用 JUnit,JavaScript 用 Jest,Go 用 testing 包——它们都支持“写测试→运行→失败→实现→再运行”这个最小闭环。第一天只练一个函数,第二天加个简单类,第三天试着 mock 一个数据库调用。节奏稳了,手感就来了。

楼下咖啡店老板娘记账用 Excel,改个公式要反复试三遍;程序员写逻辑,靠脑子硬推不如让测试替你盯梢。测试驱动开发单元测试,本质不是加工作量,是把“不确定”提前兑换成“确定”。