将单元测试添加到遗留代码中

83

在事后给旧代码添加单元测试,你有这样的经历吗?那时候代码有多复杂,每件事情都需要模拟和替身,这有多难?最终结果是否值得?


4
我会查看《遗留代码效能优化》这本书。希望它能给我一些指导,教我如何为所有这些静态依赖编写包装器! - BuckeyeSoftwareGuy
8个回答

61
我发现最好的方法是逐步增加单元测试,而不是一开始就说我们要对应用进行单元测试。
因此,如果你要修改代码,无论是为了修复错误还是重构代码,都应该首先编写单元测试。 对于错误,单元测试有助于证明问题所在,因为您可以复制它。
如果是重构代码,则需要编写单元测试,但您可能会发现很难编写测试,因此您需要找到一个高级别的函数来调用将要重构的函数,并对该部分进行单元测试。然后,在重构冒犯性函数时,编写测试以确保其按预期运行。
没有简单的方法来做到这一点。
这个问题可能会提供更多建议: 如何在大型遗留(C / C ++)代码库中引入单元测试?

9
逐步增加测试的+1。 - TrueWill

45

迈克尔·菲瑟斯的书《与遗留代码高效工作》是一本完整介绍这个主题的书。迈克尔表示,为遗留代码引入测试通常很困难,因为它没有被设计为可测试的。我从这本书中最多得到了两个名为“萌芽函数”和“萌芽类”的模式。 萌芽函数是将您需要在代码中进行的更改封装起来的函数。然后,您仅对这些函数进行单元测试。而萌芽类是相同的想法,只是新功能包含在类中。


11

是的,而且通常很痛苦。我经常不得不编写集成测试。

这本书《单元测试之道》给出了一些好的建议。它还推荐了另一本书《与遗留代码高效工作》,我还没有读过后者,但它在我的书单上。

编辑:但是,即使仅有最少的代码覆盖率也是值得的。它给了我信心和重构代码的安全保障。

编辑:我确实读了《与遗留代码高效工作》,它非常好。


4
对于《遗留代码效能提升指南》给予+1的赞同:充满了很好的建议;事实上,即使在全新开发环境下,它也非常值得阅读,作为构建可测试代码的重要资源。 - itowlson
1
+1 替换单元测试为集成测试的想法。通过适当的模拟,前者通常足够好。 - DVK

7

另外,还要看一下在遗留代码单元测试领域的新方法 - Asis项目,它受到了ApprovalTests项目的启发,并分享了其关键概念。

正如本文中提到的ApprovalTests方法:

通常情况下,你有一个巨大的遗留代码项目,没有任何测试,但你必须更改代码以实现新功能或进行重构。关于遗留代码的有趣之处在于 - 它工作!它可以工作多年,无论它是如何编写的。这是该代码的一个非常大的优势。通过批准测试,只需一个测试即可获得所有可能的输出(HTML、XML、JSON、SQL或任何其他输出),并进行批准,因为您知道 - 它可以工作!完成此类测试并批准结果后,您进行重构时会更加安全,因为现在您已经“锁定”了所有现有行为。

Asis工具的主要目的是通过自动创建和运行特征测试来维护遗留代码。
更多信息请查看以下链接:
- 项目Github仓库中详细的README文档(README) - Hacker News上的讨论(hacker news) - Reddit PHP版块的讨论(reddit.com/r/php) - Reddit编程版块的讨论(reddit.com/r/programming

这怎么没有更多的赞?如果这个仓库确实做到了它所声称的,那么这应该是被选中的答案。 - lolololol ol
它是否处理函数中的副作用,顺便问一下,这个问题是否有可能解决? - lolololol ol

5
如果你计划重构旧代码,那么创建单元测试是必须的。不要担心模拟或存根 - 关注测试系统的输入和输出,以确保您的更改或重构工作不会破坏当前功能。
我不会对你撒谎,向旧代码添加单元测试很困难 - 但这是值得的。

1
我开始为我一直在工作的遗留代码创建单元测试。然而,我意识到我一直在编写集成测试,而不是单元测试,因为我创建了实际数据并将它们插入数据库中。由于这个遗留代码根本没有为测试而设计,我看不到创建纯模拟和存根的方法。 - Vin Shahrdar

5

工作有效地处理遗留代码中提到的一种替代单元测试的方法是特性测试。我通过这些测试获得了有趣的结果。与单元测试相比,设置起来更容易,因为你可以从可以被测试的点(称为接缝)进行测试。缺点是当测试失败时,你对问题位置的线索较少,因为被测试的区域可能比单元测试大得多。在这里,记录帮助解决问题。


可以使用像xUnit家族这样的单元测试框架来编写特征测试。

在这种测试中,断言验证代码的当前行为,这些测试是在事实之后编写的。与单元测试不同的是,它们不能证明代码是正确的,它们只是确定(描述)了代码的当前行为。

该过程类似于TDD:

  • 编写一段代码的测试
  • 执行它-失败
  • 根据代码的观察行为修复测试
  • 执行它-通过
  • 重复

如果修改代码的外部行为,则测试将失败。外部行为?听起来熟悉吗?是的,在这里我们就是。现在您可以重构代码。

显然,风险取决于特征测试的覆盖范围。


5
请查看免费、开源的单元测试实用库ApprovalTests。如果您是.NET开发人员,创作者Llewellyn Falco制作了一系列视频,展示他如何使用ApprovalTests来改进新旧代码的单元测试。

1

我之前曾经谈到过在XPDays http://xpdays.com.ua/archive/xp-days-ukraine-2012/materials/legacy-code/ 上关于反转测试金字塔在遗留代码中的想法。

这个演示应该回答了一个问题:为什么有时候在处理遗留代码时,从集成/功能甚至高级验收测试开始,然后逐步引入单元测试是如此重要。没有代码示例 - 抱歉,但你可以在Michael Feathers的书籍“与遗留代码有效地工作”中找到一堆示例。

此外,您还可以查看Legacy Code Retreat http://www.jbrains.ca/legacy-code-retreat 并寻找您所在地区的会议。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接