【问题标题】:How do i test/refactor my tests?我如何测试/重构我的测试?
【发布时间】:2013-03-22 08:49:10
【问题描述】:

我的应用有一套测试服。随着测试套件的有机增长,测试中有很多可以重构的重复代码。

但是,我想确保测试套件不会随着重构而改变。如何测试我的测试与重构保持不变。

(我使用的是 Python+UnitTest),但我想这个问题的答案可能与语言无关。

【问题讨论】:

  • 为了快速而肮脏的解决方案,您可以尝试在重构之前/之后检查覆盖率。

标签: python unit-testing testing language-agnostic


【解决方案1】:

真正的测试是生产代码。

检查测试代码重构是否没有破坏您的测试的有效方法是执行Mutation Testing,其中对被测代码的副本进行变异以引入错误,以验证您的测试是否捕获错误。这是一些测试覆盖工具使用的策略。

我没有用过它(而且我也不是真正的 python 编码器),但这似乎得到了Python Mutant Tester 的支持,所以这可能值得一看。

【讨论】:

    【解决方案2】:

    Coverage.py 是你的朋友。

    将您要重构的所有测试移至“系统测试”(或某些此类标记)。重构你想要的测试(你会在这里做单元测试吗?)并监控覆盖率:

    • 在运行新单元测试之后但在运行系统测试之前
    • 在运行新的单元测试和系统测试之后。

    在理想情况下,覆盖率将相同或更高,但您可以推翻旧系统测试。

    FWIW,py.test 提供了轻松标记测试和只运行特定测试的机制,并且与 unittest2 测试兼容。

    【讨论】:

      【解决方案3】:

      有趣的问题 - 我总是热衷于听到“我如何测试测试?!”类型的讨论。以及上面@marksweb 的优点。

      检查您的测试是否确实在执行您希望他们执行的操作并测试您的意图始终是一个挑战,但最好做到这一点并正确执行。我总是尝试考虑一个经验法则,即测试应该占任何项目中1/3 的开发工作...无论项目时间限制、压力和不可避免地出现的问题如何。

      如果您打算继续并发展您的项目,您是否考虑过像您所说的那样进行重构,但以创建适当的测试框架的方式进行重构,该框架允许测试驱动开发 (TDD) 任何未来添加的功能或项目的一般扩展?

      【讨论】:

        【解决方案4】:

        虽然您提到了 Python,但我想评论一下如何在 Smalltalk 中应用重构。大多数现代 Smalltalk 实现包括一个集成在系统浏览器中的“Refactoring Browser”,用于重构源代码。 RB 包含一个重写框架,用于动态执行您询问的有关保持系统行为和稳定性的转换。使用它的一种方法是在通过差异工具提交之前打开一个作用域浏览器,应用重构并查看/编辑更改。我不知道 Python 重构工具的成熟度,但 Smalltalk 社区花了很多迭代周期(年)才拥有如此出色的软件。

        Don Roberts 和 John Brant 编写了第一个重构浏览器工具,现在它已成为重构工具的标准。有一些 videoshere 展示了其中一些功能。要将方法提升为超类,在Pharo 中,您只需选择方法、重构和“拉起”菜单项。该规则将检测并让您在执行之前检查建议的重复子实现程序以进行删除。重构的应用与测试代码无关。

        【讨论】:

          【解决方案5】:

          理论上你可以为测试编写一个测试,模拟被测的实际对象。但我想这只是做很多工作的方式,不值得。

          所以剩下的就是一些策略,它们会有所帮助,但不会确保失败。

          1. 工作要非常小心和缓慢。尽可能使用 IDE 的功能以减少人为错误的可能性。

          2. 两人一组。一个看着你的搭档可能会发现你错过的故障。

          3. 复制测试,然后重构它。完成后在生产代码中引入错误以确保两个测试以相同(或等效)的方式找到问题。然后才删除原来的测试。

          4. 最后一步可以通过工具来完成,虽然我不知道 python 的风格。要搜索的关键字是“突变测试”。

          说了这么多,我个人对步骤 1+2 很满意。

          【讨论】:

            【解决方案6】:

            我看不到重构测试套件的简单方法,并且根据重构的程度,您显然必须更改测试套件。你的测试套件有多大?

            正确的重构需要时间和对细节的关注(还有很多 Ctrl+C Ctrl+V !)。每当我重构我的测试时,除了查找和替换之外,我不会尝试找到任何快速的方法,因为这涉及太多风险。

            如果您想保持测试的质量,最好手动正确地做事,尽管速度慢。

            【讨论】:

              【解决方案7】:

              不要重构测试套件。

              重构的目的是让代码更容易维护,不是满足“代码美观”的一些抽象标准。测试代码不需要很好,不需要避免重复,但它确实需要彻底。一旦你有一个有效的测试(即它确实测试了被测代码的必要条件),你不应该删除它或更改它,因此测试代码不需要易于维护。

              如果您愿意,您可以重写现有的测试以使其变得更好,并运行新的测试除了旧的测试。这保证了新的组合测试套件能够捕捉到旧版本所做的所有错误(可能还会更多,因为您将来扩展新代码时)。

              有两种方式可以将测试视为无效 - 您意识到它是错误的(即,有时测试中的正确代码会错误地失败),或者测试中的接口已更改(删除测试的 API,或允许以前测试失败的行为)。在这种情况下,您可以从套件中删除测试。如果您意识到一大堆测试是错误的(因为它们包含错误的重复代码),然后您可以将它们全部删除并用重构和更正的版本替换它们。您不会因为不喜欢其源代码的风格而删除测试。

              要回答您的具体问题:要测试您的新测试代码是否等同于旧代码,您必须确保 (a) 所有新测试都通过您当前正确的测试代码已知代码库,这很容易,而且 (b) 新测试检测旧测试检测到的所有错误,这通常是不可能的,因为您手头没有被测代码的一套错误实现。

              【讨论】:

              • 我完全同意并可能补充说,当测试用例过时时,不应将其删除,而应使用合适的 @skip... 装饰器进行标记。这是用于文档,并允许您为旧版本的代码保持测试用例处于活动状态。 docs.python.org/2/library/…
              • 我不同意测试代码不需要“好”。一旦它应该服务于文档的目的。如果你在做敏捷 TDD,它也是一种设计工具。如果你不尽量减少测试中的重复,如果你碰巧更改了被测代码,它们很可能会变得很麻烦。
              • @nansen:这不是我的经验,重复的测试从来没有给我带来任何特别的麻烦。此外,我会担心是否有人对被测代码进行向后不兼容的更改,然后假设仅仅因为他们已经找到并更新了一个测试,“没有重复,所以我完成了”。既然你提到了文档——书面文档是另一件事,我不会强制重构以避免所有重复的文本......
              • 我没有将测试的记录特征与重复或减少相关联。我的意思是干净和可读的测试是被测试代码的有价值的文档。提高可读性是重构的一个重要目标。
              • .. 但是自从争论开始以来:重复的文档是另一个 PITA,原因与重复的文本完全相同:一旦文档必须更改(比如明天),有人必然会只更新一半的地方.
              【解决方案8】:

              测试代码可以成为 API 的最佳低级文档,因为只要它们通过且正确,它们就不会过时。但是凌乱的测试代码并不能很好地达到这个目的。所以重构是必不可少的。

              您的测试代码也可能会随着时间而改变。测试也是如此。如果您希望它顺利进行,则必须尽量减少代码重复,并且可读性是关键。

              测试应该易于阅读,并且总是一次测试一件事并明确以下内容:

              • 先决条件是什么?
              • 正在执行什么?
              • 预期的结果是什么?

              如果考虑到这一点,重构测试代码应该是相当安全的。一步一步,正如@Don Ruby 所提到的,让您的生产代码成为测试的测试。

              对于许多重构,您通常可以安全地依赖高级 IDE 工具 - 如果您要注意提取代码中的副作用。

              虽然我同意应避免在没有适当测试覆盖的情况下进行重构,但我认为在通常情况下为测试编写测试几乎是荒谬的。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2023-02-15
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-09-06
                • 1970-01-01
                • 1970-01-01
                • 2013-04-27
                相关资源
                最近更新 更多