为什么你应该今天(而不是明天)转向测试驱动开发

Fred· AI Engineer & Developer Educator1 min read

大多数开发者都听说过测试驱动开发。大多数人没有认真尝试过。尝试过的人要么喜欢它,要么错误地尝试过然后放弃了。TDD 不是关于写测试。它是关于更快地写出更好的代码。关键是要去做。

每个人都搞错了什么

TDD 不是"写测试,然后写代码"。它是"写一个失败的测试,写最少的代码使其通过,然后重构"。重构步骤很重要。大多数人跳过它。这就是为什么他们认为 TDD 很慢。

红-绿-重构是循环。写一个失败的测试(红色)。只写足够通过的代码(绿色)。清理你制造的混乱(重构)。重复直到功能完成。测试给你重构而不破坏东西的许可。

第一次尝试 TDD 的人通常会写一个测试,然后写整个功能,然后想知道为什么测试立即通过了。你应该先看着它失败。失败的测试证明你的测试测试了某些东西。

为什么它有效

TDD 迫使你在写代码之前思考代码将如何被使用。如果你的测试很难写,你的 API 可能很糟糕。修复 API。这是设计压力。这是 TDD 的主要好处,大多数人完全错过了它。

# 没有 TDD,你可能会写这个
def process_user_data(user_id):
    db = Database()
    conn = db.connect()
    user = conn.query("SELECT * FROM users WHERE id = ?", user_id)
    # 另外 50 行数据库逻辑与业务逻辑混合
    return result

# 有了 TDD,测试迫使你分离关注点
def test_process_user_data():
    user = User(id=1, name="Test")
    result = process_user_data(user)
    assert result.status == "processed"

第二个版本是可测试的,因为测试迫使你将数据访问与业务逻辑分离。你无法轻松测试第一个版本而不连接真实数据库。TDD 通过使糟糕的设计难以测试来推动你走向更好的设计。

时间问题

"我没有时间写测试"是倒过来的。TDD 节省时间。这是数学。没有测试,你写代码,手动测试它,发布它,在生产中发现 bug,上下文切换回来修复它们,并希望你没有破坏其他东西。

有了 TDD,你写测试,写代码,然后你完成了。测试在你仍在代码中时立即捕获 bug。没有上下文切换。没有生产火灾。没有"我以后会测试它"但永远不会发生。

TDD 的第一周是慢的。你在学习。之后,你比以前更快。一个月后,你想知道你怎么可能在没有测试的情况下发布代码。投资回报是以天为单位衡量的,不是月。

测试什么

测试行为,不是实现。不要测试一个函数调用另一个函数。测试当你调用函数时,你得到正确的结果。实现细节可以改变。行为应该保持一致。

// 糟糕的测试 - 测试实现
test('user registration calls database.insert', () => {
  const db = mock(Database)
  registerUser(db, 'test@example.com')
  expect(db.insert).toHaveBeenCalled()
})

// 好的测试 - 测试行为
test('user registration creates an account', () => {
  registerUser('test@example.com', 'password')
  const user = findUserByEmail('test@example.com')
  expect(user).toBeDefined()
  expect(user.email).toBe('test@example.com')
})

第一个测试在你每次重构用户存储方式时都会中断。第二个测试只在注册停止工作时才会中断。测试行为。

当 TDD 很难时

TDD 在探索性代码上有困难。如果你还不确定你在构建什么,先写一次性代码。一旦你搞清楚了,删除所有内容并用 TDD 重新做一遍。第二次总是更快更干净。

UI 代码很棘手。你可以 TDD UI 背后的逻辑,但测试"按钮是蓝色的"通常不值得。测试点击按钮做正确的事情,而不是按钮看起来正确。

没有测试的遗留代码是噩梦。你不能轻松地 TDD 对未测试代码的更改。解决方案是添加特征测试来记录代码当前做什么,然后 TDD 新功能。随着时间的推移,代码库变得更好。

工具不太重要

每种语言都有测试框架。JavaScript 的 Jest。Python 的 pytest。Java 的 JUnit。PHP 的 PHPUnit。它们都工作得很好。选择你的语言中最流行的然后继续前进。工具不会使 TDD 工作。纪律会。

快速测试比框架更重要。如果你的测试需要 10 秒才能运行,你不会经常运行它们。如果可能的话,保持测试在一秒以内。对像数据库和 HTTP 调用这样慢的东西使用模拟。快速反馈使 TDD 愉快。慢速反馈使它痛苦。

明天开始是错误的

开始 TDD 的最佳时间是你的第一个项目。第二好的时间是现在。选择你需要写的下一个函数。先写测试。看着它失败。让它通过。重构。再做一次。

不要试图立即 TDD 一切。不要回去为旧代码添加测试,除非你正在更改它。从新代码开始。一次一个函数。逐渐养成习惯。几周后,它变得自动。

从不开始 TDD 的开发者一直在犯同样的错误。开始 TDD 然后一天后放弃的开发者从未给它真正的机会。坚持两周的开发者通常会永远坚持下去。

TDD 不是银弹。它本身不会修复糟糕的代码或糟糕的架构。但它会使你的代码更好,你的 bug 更少,你的信心更高。今天开始。

相关阅读

想要提升你的开发职业?查看:

Fred

Fred

AUTHOR

Full-stack developer with 10+ years building production applications. I write about cloud deployment, DevOps, and modern web development from real-world experience.

Need a developer who gets it?

POC builds, vibe-coded fixes, and real engineering. Let's talk.

Hire Me →