TDD 的原理和使用场景

4,229 阅读6分钟

前言

哈喽,大家好,我是海怪。

说起前端测试,有一个东西肯定是逃不掉的,那就是 TDD —— 测试驱动开发。很多前端大佬也都非常喜欢用 TDD 的模式来编程。因为它不仅可以通过测试保障代码质量,还能创造一个良好的开发环境来提高开发效率。

然而,有些同学会对此嗤之以鼻,觉得先写测试再写业务不是浪费了那 50% 的工时么?根本没时间写业务代码呀。我觉得这部分同学其实并没有搞清楚 TDD 的适用场景以及它要解决的问题。正好 Kent C. Dodds 在他这篇 《When I follow TDD》 里聊了关于 TDD 的一些想法和思路,今天就把这篇文章分享给大家~

翻译中会尽量用更地道的语言,这也意味着会给原文加一层 Buf,想看原文的可点击 这里


正片开始

测试驱动开发(TDD)包含了 3 个步骤,一般也被称为 “红,绿,重构循环”。

下面是它的工作原理:

  • 🚨 红色部分:在你还没添加新功能前先写一个测试。然后你会得到一个失败的测试用例(会看到 “红色” 的报错信息)。
  • ✅ 绿色部分:慢慢添加业务代码来让测试通过(看到 “绿色” 成功通过信息)。
  • 🌀 重构部分:再回过头看审视自己的代码,把它重构成高可读性和高维护性的代码(这一步最棒的地方在于之前写的测试用例会告诉你在重构时是否会破坏现有逻辑)。
  • 🔁 重复:这就是个循环,反正 😉 一直走下去,直到写完这个功能

在真实使用上,这个方法可能有所不同,有些人还会把 TDD 作为自己的开发信仰。而我会站在更实用的角度上使用 TDD,只在一些我觉得有好处的情况下使用它。

那么问题来了:“什么时候用 TDD 才是合理的呢?”。这其实很依赖你的开发直觉。坦率地说,这跟你用 TDD 的感觉和经验有很大关系。当然,也有一些我经常会用 TDD 的经典场景。

修 Bug 场景

当在修 Bug 时,我喜欢在修复之前先写一个测试来复现它。这么做可以给我带来非常大的信心,让我在通过测试后马上知道是什么原因导致的这个 Bug,这样一来,我就知道我实际上已经修复了这个错误,而不仅仅是围绕这个问题进行了测试。

在维护我比较关注的软件时,90% 的时间都遵循这种方法(并因此添加了测试)。特别是在我的开源项目中就这么做的。这是这类测试的一个例子。

要修 Bug 么?试试 TDD 吧。

纯函数场景

我不会测所有的工具纯函数(对大部分纯函数我会用集成测试来覆盖),不过,如果某个工具函数有足够的复杂度,而且必须要用隔离的单测来测,那这也是一个使用 TDD 的绝佳机会。一般这类函数,你代码里都会有定义比较清晰的输入和输出结构。

我想大多数人都经历过这样的情况(就算现在没有,以后会也有的)。以前我在 PayPal 的时候,我要在用户输入对应的金额准备转账时做格式转换。由于要考虑货币的精度,这个处理逻辑比你相像得要复杂得多(有的货币根本没有小数概念)。对货币金额做格式化就是一个做 TDD 很好的例子,因为输入和输出都是很容易想出来的。

另一个很好的例子就是 我的项目 rtl-css-js 的测试(这也是开源的)。

要准备写纯工具函数么?试试 TDD 吧。

定义良好的交互场景

直到我创建了 Testing Library 后,我才认为用户界面的 TDD 在 Web 上确实可行,因为:

当你在 测代码实现细节 时,做 TDD 是没有意义的。

老实说,如果你在测代码实现细节,做任何测试都是没有意义的(它们只会拖慢你的速度)。TDD 一部分的意义在于帮助你思考:如何从在不考虑细节情况下从外部构建你的应用,这样你就会在设计项目时盯住你的主要目标,而不会钻入牛角尖。当你知道要做什么而不是想知道要怎么做的时候,它会对你有所帮助。

在 Testing Library 出来前的一些流行工具(所有测试工具种类),它能够让你(鼓励你)去测实现细节。如果这时你要用 TDD,你就得知道(比如)你要创建一个叫 makeDonation 的私有方法,调用时,它会分别传入(而不是传反) amountcurrency 两个参数。这也导致人们总感觉做 TDD 纯属浪费时间,只是走走过场。

不过现在 Testing Library 可以让你关注于用户交互,而不是实现细节,你可以在设计和定义好用户交互后使用 TDD。

几年前我录的一个视频, 里面用 Login 组件展示了这样的方法。这已经是几年前的了,现在应该更容易实现。

要准备设计一个定义明确的 UI 么?试试 TDD 吧。

总结

到这里说差不多了。我敢肯定,其他人在做 TDD 实践时也有他们自己觉得合理的场景,这也挺好的。

如果我只是写点试验代码片段(我经常这么干)或者只是乱写写代码,那我肯定不会用 TDD 的。只有在项目在往正道发展时,我才会添加对应的测试。顺便说一下,我在使用类型检查工具时也是这么干的。这也是我一直遵循的 抽象思路

写测试,添加类型定义,对代码做抽象都是对你项目的投资。如果你不确定创建的东西是否会长期存在,那么进行这些投资是没有意义的。如果你不确定在你完成时你创造的东西最终会变成什么样,那么这些投资也可能是不明智的。还有就是这些错误的投资所造成的沉没成本最终也会沦为一些不优雅的解决方案,最终会影响你的一些判断。


好了,这篇外文就给大家带到这里了。文章里主要讲了 3 种使用 TDD 的场景:修 Bug 时,写纯函数时,以及设计 UI 时。我感觉在写纯函数(数据转换),以及写接口时(Node 端开发)时用的比较多,修 Bug 嘛,实际情况都是业务 Bug,要用测试复现是比较麻烦的。设计 UI 前写测试也是比较麻烦的。总之,大家应该都会有自己使用 TDD 的场景,找到适合自己的就好。需要注意的是,千万别提前做优化,特别是你还不确定你的项目要发展到什么程度时。

如果你喜欢我的分享,可以来一波一键三连,点赞、在看就是我最大的动力,比心 ❤️