测试对抗遗留问题

自动化测试是对抗遗留代码的最佳工具。

通过自动测试(如单元测试或行为测试),您可以有效地重构遗留代码,并确信不会有什么问题。

编写糟糕的系统通常由紧密耦合的函数组成。对一个类中的函数进行修改,很可能会破坏另一个完全不同的类中的函数,从而产生多米诺骨牌效应,导致更多的类被破坏,直至整个应用程序被破坏。

为了解耦类并遵循单一责任原则等实践,必须进行重构。任何重构工作都必须确保不会破坏应用程序中其他地方的代码。

这就引出了测试覆盖率的话题:测试覆盖率是一个真正有意义的数字吗?

阿尔贝托-萨沃亚(Alberto Savoia)在artima.com 网站上发表了一则有趣的轶事,对这个问题做出了最好的回答;让我们一起来读一读吧:

一天清晨,一位程序员向大师请教:"我准备编写一些单元测试。我的目标是多大的代码覆盖率?

大师回答说 "不用担心覆盖率,只要写一些好的测试就可以了。"

程序员微笑着鞠了一躬,然后离开了。

......

那天晚些时候,第二个程序员提出了同样的问题。大师指着一锅沸腾的水说 "我应该在锅里放多少粒米?" 程序员一脸困惑地回答:"我怎么能告诉你呢?这取决于你需要养活多少人,他们有多饿,你还提供了哪些食物,你有多少米可用,等等。"

"没错。"大师说。

第二个程序员微笑着鞠了一躬,然后离开了。

......

一天快结束时,第三个程序员来了,问了关于代码覆盖率的同样问题。

"百分之八十,不能再少了!" 主编程员用拳头重重地敲了一下桌子,厉声回答道。

第三个程序员微笑着鞠了一躬,然后离开了。

......

在最后一次回答之后,一位年轻的学徒找到了大师: "大师,今天我无意中听到您在回答关于代码覆盖率的同一个问题时,有三个不同的答案。为什么呢?

大师从椅子上站了起来: "来和我一起喝杯新茶,我们来谈谈这个问题。" 两人在茶杯里斟满冒着热气的绿茶后,大师开始回答: "第一个程序员是新人,刚刚开始测试。现在他有很多代码,却没有测试。他还有很长的路要走;此时关注代码覆盖率会让人沮丧,而且毫无用处。他最好习惯于编写和运行一些测试。他可以稍后再担心覆盖率的问题。"

"另一方面,第二位程序员在编程和测试方面都很有经验。当我问她应该在锅里放多少粒米时,我帮助她认识到,必要的测试量取决于很多因素,而她比我更了解这些因素--毕竟这是她的代码。没有一个单一的、简单的答案,她很聪明,能够处理真相,并与之合作。"

"我明白了,"小学徒说,"但如果没有单一简单的答案,那你为什么要回答第三个程序员'八成也不能少'呢?"

大师笑得很开心,笑得很大声,证明他不止喝了绿茶的肚子上下翻腾。"第三个程序员只想要简单的答案--即使没有简单的答案......然后无论如何也不遵守。"

年轻的学徒和灰头土脸的大师在沉思中喝完了茶。

Alberto 传达了一个简单的信息:专注于拥有尽可能多的业务逻辑和功能才是最好的出路。测试覆盖率并不是一个任意的数字。

有些事情不测试是合理的,即使是已经测试过的代码也有不同的逻辑路径。

此外,在分布式系统中,应用程序接口或系统之间的通信可能会破坏系统。在分布式架构中,仅测试代码可能是不够的。强大的监控系统变得至关重要。确保一致部署和升级的 "基础架构即代码" 变得尤为重要。此外,实现松散耦合的服务和适当的进程间通信比一些单元测试更有利于整体架构。

除了测试驱动开发(TDD),还有另一种方法。行为驱动开发(BDD)为我们提供了一种不同的代码测试机制;让我们来讨论一下它。