2019年/10月/30日

首页回退

单元测试的好处

这是一篇很老的文章,写于2006年,关于要写测试这个习惯我悲观的估计在国内可能再过十年都未必能流行起来 观念不改,止步不前

原文

单元测试证明你的代码真的可用。

这意味着你的代码很少有bug。不,单元测试不能替代系统测试和验收测试。但是它们确实补充了它。更少的bug,让软件质量保证(Software Quality Assurance, SQA)变得更好。

你得到一个低层级的回归测试套件。

你可以随时重复执行并且看到不止是出现了什么错误,而且能知道bug出在哪里。很多团队将执行单元测试做为常规编译的一部分。这是一种低成本的在进入系统性测试编译之前抓出bug的方式。

你能在没有破坏设计的情况下改进它。

这其实是上面提到的第三步(重构)的一部分。因此,测试先行的代码通常不需要重构。我曾接手过一些非常神经质的系统,像一个精神错乱的人一样,你都不能理清它们。拥有单元测试,你可以做非常强大的重构,这些重构可以让你从大多数精神错乱系统的挑战中脱身。

写单元测试让写代码变得更加有趣。

你知道你的代码需要做什么。然后你让它去做。甚至如果你没有一个正常工作的系统,你也可以看到你的代码真正运行起来并且可以正常工作的样子。你得到一种非常强烈的“我成功了!”的成就感。现在每分钟重复执行一次。如果你想变得非常嗨,为你的工作感到骄傲,以此激励持续前进,尽管尝试测试先行的开发模式吧!

它们展现出实质性的工作进展。

你不必为了让系统的所有部分都组装到一起等上一个月时间。你可以展示当前进度,甚至不需要有一个真正运行的系统。你不仅能说你已经把代码写好了,而且你可以实实在在地展示效果。当然,这是另一个传统编程教我们忽略的目标。“完成”不意味着你把代码写完了然后提交。“完成”意味着代码在系统中没有bug地真正跑起来了。运行单元测试是让我们更接近后者的一步。

单元测试是示例代码的一部分。

我们都遇到过一些我们不知道怎么用的方法和类库。第一个我们要找的地方就是示例代码。示例代码就是文档。但是我们并不总是能在内部代码中找到示例代码。所以我们接着通过源码或者系统的其他地方详细查找有没有示例代码。因为张三,那个写这些代码的人已经离职了,所以我们没办法去问他这些方法和类是怎么运作的。但是单元测试就是文档。所以当我们记不清如何使用类 Foo 的时候,在单元测试里找找看。

测试先行迫使你在写代码之前做了计划。

先写测试迫使你在写代码之前思考你的设计和它必须达到什么结果。这不仅使你专注,而且能得到更好的设计。

测试先行减少 bug 的成本。

Bug 越早发现就越容易修复。Bug 较晚被发现通常是由多个变化引起的,我们并不知道哪一个变化引发的这个 bug。所以,首先我们必须尽快找出bug。因此,我们必须刷新关于代码应该如何工作的记忆,因为我们有好几个月没见过它了。最终我们有了足够的理解以提出一个方案。任何可以减少写下bug和检测到它之间的时间的事情看起来都是明显的胜利。我们认为我们自己足够幸运,在将代码移交给 SQA 或者客户的之前,能够在几天内找到bug。但是几分钟内就能抓住这些bug感觉如何?这就是测试先行能完成的事情。

它甚至比代码检测更好。

他们说,代码检测比测试更好,因为使用代码检测来探测和修复bug比测试更划算。当代码移交之后,修复bug将更加昂贵。我们越早探测到bug并修复它,就越容易、越省事和越好。这就是代码检查(code review)的优势:代码检测在几天内捕获更多的bug,而不是几个月。但是测试先行可以在几分钟内捕获一些bug而不是几天。它甚至比代码检测更省事。

它实际上消除了编程人员的灵感枯竭问题。

有没有想过接下来要写什么声明?就像作家的文思枯竭一样,程序员的灵感枯竭也是一个实实在在的问题。但是测试先行将代码的结构化部分系统化,让你可以专注于创造性的部分。你可能会受困于如何测试下一部分或者如何让当前的测试通过,但是你永远不会为下一步要做什么的问题感到困惑。

单元测试造就更好的设计。

测试一小部分代码迫使你定义这些代码要负责什么。如果你可以容易地做到这点,就意味着代码的职责是定义良好的。因此,它具有高内聚的特性。如果你可以对你的代码进行单元测试,那么就意味着你可以像绑定到测试一样容易地将它与系统的其他部分进行绑定。所以,它与它周围的其他部分是低耦合的。高内聚低耦合是优秀的、可维护的设计的定义。那些容易进行单元测试的代码也是易于维护的。 它比没有测试地写代码更快! 或者换种方式来说,除非你真的需要代码正常工作,否则跳过单元测试是更快的。我们在代码上花费的大多数工夫花在了将它提交到源码仓库之后修复它。但是测试先行通过允许我们从一开始就获得更多正确的信息,并使错误更容易修复,消除了许多的浪费。

另一篇