Zi莱卷

此时的庸忙、诺诺慌张, 可否已成你的日常。

啥时候用TDD

2020-07-25 21:11:43

TDD 不是任何时候都适用,本文讲了它(对我而言)适用的场景。

测试驱动开发(又称 TDD)分为三步,通常被称为「红、绿、重构」。

TDD

下面是它的工作方式:

  • 🚨 : 在添加功能前,先写测试。这个测试会失败(你会看到"红色"的失败信息)
  • 绿: 写刚好够通过测试的代码。(你会看到"绿色"的成功信息)
  • 🌀 重构: 重构之前完成的代码,确保它没有坏味道且易于理解。(这个步骤中神奇的事情是如果你把代码改坏了,会有测试给你反馈)
  • 🔁 重复: 这是一个循环,你需要一直重复上面的步骤直到你完成需要开发的功能

这种方式存在很多的细微差别,但是很多人喜欢无脑吹捧它。我只在我觉得有益的场景下才会去实际的使用它。

I write test following TDD when it make sense. - Kent C. Dodds

但这是个复杂的问题:"到底什么时侯采用 TDD 才是有意义的?"。这真的得凭直觉,和你使用 TDD 的熟练度有很大的关系。这里有几个我使用 TDD 的情景。

修复一个 Bug

当我需要修复一个 Bug 的时侯,我喜欢在修复它之前先写一个测试来重现 Bug 产生的原因。这样做给了我巨大的自信 - 那就是我真正理解了的 Bug 产生的原因。并且当我将测试变绿时,我知道我已经真正的修复了那个问题而不需要再去测试其他的什么东西了。

我想说的是,在我负责的软件中,我 90%的时间都会遵循这种方法修复 Bug(因此要进行测试)。尤其是在我的开源库中。这是一个例子

需要修复 Bug, 试试 TDD 吧。

纯函数

我不会测试我全部的纯工具型的函数(很多我都会用集成测试),但是如果我有一个足够复杂的工具函数,就需要单独的单元测试。这也是一个非常适合使用 TDD 的场景。对于这些类型的函数,你通常会根据你对代码的要求,有一套非常明确的输入和输出。

我想我们大多数人都经历过这样的情况。当我在 Paypal 的时侯,我需要格式化金额。那个功能的逻辑是惊人的复杂,主要是由于货币精度(一些货币没有小数点金额的概念)。但这样的货币金额格式化对于 TDD 来说是完美的情况,因为可能的输入和所需的输出很容易产生。

另一些这种情况的示例(那也是一个开源项目)是: 对 rtl-css-js 的测试

编写纯工具函数?试试 TDD 吧。

明确定义的 UI

自从我创建了 Testing Library 我才觉得 Web 用户界面的 TDD 真的是合理可行的。原因是:

当你测试 实现细节 时,TDD 毫无意义。

老实说,如果你正在测试实现细节,那根本就没有意义(这只会让你放慢速度)。使用 TDD 的目的是帮助你从外部考虑正在构建的事物,而无需考虑实现细节的问题。因此,在实现具体需求时,不会在代码细节中迷失方向,并且可以牢记高层目标。当你知道你要做什么,而不是怎么做时,TDD 才可以帮助你。

在 Testing Library 之前的所有流行的测试工具都允许(鼓励)你测试实现细节。因此,你在之前使用 TDD 的时侯,需要知道(例如)你要创建一个名为makeDonation 的私有方法,并且它被调用时会传递 amountcurrency(而不是相反)。所以你在用 TDD 的时侯总觉得是在浪费时间,毫无意义。只是走走过场而已。

然而, 由于 Testing Library 需要你关注用户体验,而不是实现细节。所以在构建具有明确定义的设计和用户体验的 UI 时,你可以使用 TDD。

这有一个例子,这是我几年前制作的一个 视频 。它有一些旧了,如果你想看最新的视频,请看 TestingJavaScript.com

构建明确定义的 UI? 试试 TDD 吧。

总结

对我来说差不多就是这样了。我相信其他人也有应用 TDD 实践的有效场景,这很好。

如果我只是在做一些探索性的编码(我经常这样做),或者是乱七八糟的事情,那么我就不会去遵循 TDD,只有当我对事情的发展方向感到满意时,我才会添加测试。顺便说一下,这也是我在使用类型系统(如 Flow 或 TypeScript)时遵循的方法。这也是我在 做抽象 的时候遵循的方法。

编写测试、添加类型、为你的代码做抽象是对你所构建的东西的投资。如果你不确定你所构建的东西是否会长期存在,那么进行这些投资是没有意义的。如果你不确定你所建立的东西在你完成后会以什么形式存在,那么这些投资也是不明智的。而更进一步说,这些投资的沉没成本谬误会影响你,从而导致一个次优的解决方案。

原文链接:https://kentcdodds.com/blog/when-i-follow-tdd

🔖TDD