【问题标题】:Adding unit tests to legacy code [closed]向遗留代码添加单元测试[关闭]
【发布时间】:2010-12-05 05:20:05
【问题描述】:

您是否曾经在遗留代码中添加单元测试?代码有多复杂,存根和模拟所有内容有多困难?最终结果值得吗?

【问题讨论】:

标签: unit-testing legacy-code


【解决方案1】:

我发现最好的方法是逐步添加单元测试,而不是直接说我们现在将对应用程序进行单元测试。

因此,如果您要接触代码、进行错误修复或重构,请先编写单元测试。对于错误,单元测试将有助于证明问题出在哪里,因为您可以复制它。

如果要重构,你会想写单元测试,但是你可能会发现测试是不可能写的,所以你可能需要找一个高层,调用将要重构的函数,对那部分进行单元测试.然后,在重构攻击性功能时,编写测试以确保它按应有的方式运行。

没有简单的方法可以做到这一点。

这个问题可能有助于提供更多建议。 How do you introduce unit testing into a large, legacy (C/C++) codebase?

【讨论】:

  • +1 用于增量添加测试。
【解决方案2】:

Michael Feathers 的书“有效地使用遗留代码”是一本书,涵盖了这个主题。 Michael 指出,为遗留代码引入测试通常太难了,因为它的结构不是可测试的。我从书中得到最多的是一些名为“Sprout 函数”和“Sprout 类”的模式。 Sprout 函数是一个封装了您需要在代码中进行的更改的函数。然后,您只需对这些功能进行单元测试。除了新功能包含在一个类中之外,新芽类是相同的想法。

【讨论】:

    【解决方案3】:

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

    The Art of Unit Testing 这本书对此有一些很好的建议。还推荐书Working Effectively with Legacy Code;我还没有读过后者,但它在我的堆栈中。

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

    编辑:我确实读过有效地使用遗留代码,它非常好。

    【讨论】:

    • +1 表示“有效地使用旧代码”:充满了很好的建议;事实上,即使对于未开发的环境,它也值得一读,因为它是构建可测试性代码的重要资源。
    • +1 表示用集成测试替换单元测试的想法。通过适当的嘲笑,前者已经足够好,经常
    【解决方案4】:

    看看遗留代码单元测试领域的新方法 - Asis project,它的灵感来自 ApprovalTests 项目并分享了它的关键概念。

    正如this article 中提到的 ApprovalTests 方法:

    通常您有一个庞大的遗留代码项目,而您没有在其中进行任何测试 全部,但您必须更改代码以实现新功能,或者 重构。遗留代码的有趣之处在于——它有效!它 工作多年,不管它是如何写的。这是一个非常棒的 该代码的优势。获得批准,只需一项测试即可获得 所有可能的输出(HTML、XML、JSON、SQL 或任何可能的输出 是)并批准,因为你知道 - 它有效!完成后 这样的测试并批准了结果,您确实更安全 重构,因为现在您“锁定”了所有现有行为。

    Asis 工具正是通过自动创建和运行特征测试来维护遗留代码。

    更多信息请看

    【讨论】:

    • 这怎么没有更多的赞成票?如果 repo 做了它声称的事情,这应该是选择的答案。
    • 它是否处理函数中的副作用,顺便说一句?甚至有可能解决这个问题?
    【解决方案5】:

    如果您计划重构遗留代码,那么创建这些单元测试是必须的。不用担心 mocking 或 stubbing - 不用担心测试系统的输入和输出,这样您的更改或重构工作就不会破坏当前的功能。

    我不会骗你,将单元测试改造成遗留代码很困难 - 但这是值得的。

    【讨论】:

    • 我开始为我一直在处理的遗留代码创建单元测试。但是,我意识到我一直在编写集成测试,而不是单元测试,因为我创建了实际数据并将它们插入到数据库中。我看不到为这个遗留代码创建纯模拟和存根的方法,因为它根本不是为测试而构建的。
    【解决方案6】:

    characterization tests 是单元测试的一种替代方法,在有效处理遗留代码中也有介绍。通过这样的测试,我得到了有趣的结果。它们比单元测试更容易设置,因为您从点测试而不是可以测试(称为接缝)。缺点是当测试失败时,您对问题位置的提示较少,因为被测区域可能比单元测试大得多。日志记录在这里有所帮助。


    可以使用 xUnit 系列的单元测试框架来编写特性测试。

    在这样的测试中,根据事实编写,断言验证代码的当前行为。与单元测试不同,它们并不能证明代码是正确的,它们只是确定(表征)代码的当前行为。

    过程与TDD类似,:

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

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

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

    【讨论】:

      【解决方案7】:

      看看免费的开源单元测试实用程序库ApprovalTests。如果您是 .NET 开发人员,那么创建者 Llewellyn Falco 已经创建了 series of videos,展示了他如何使用 ApprovalTests 来改进新代码和旧代码的单元测试。

      【讨论】:

        【解决方案8】:

        我前段时间在 XPDays http://xpdays.com.ua/archive/xp-days-ukraine-2012/materials/legacy-code/ 上谈论过旧代码中的反向测试金字塔的想法

        本演示文稿应该回答为什么在处理遗留代码时有时从集成/功能甚至高级验收测试开始如此重要的问题。然后慢慢地,一步一步地引入单元测试。没有代码示例 - 抱歉,但您可以在 Michaels Feathers 的书“有效地使用旧代码”中找到其中的一堆。

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

        【讨论】:

          猜你喜欢
          • 2014-03-23
          • 1970-01-01
          • 2010-09-10
          • 1970-01-01
          • 2020-06-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多