9

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

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

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

4

8 回答 8

5

测试的真正测试是生产代码。

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

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

于 2013-03-22T20:50:18.877 回答
3

Coverage.py 是你的朋友。

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

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

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

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

于 2013-03-26T23:04:41.900 回答
2

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

检查你的测试是否真的在做你想让他们做的事情并测试你想要做的事情总是一个挑战,但是把它做好并正确地做是很好的。我总是试图考虑一个经验法则,即测试应该占任何项目开发工作的 1/3……不管项目时间限制、压力和不可避免地出现的问题。

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

于 2013-03-22T09:23:28.913 回答
1

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

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

于 2013-03-23T04:14:19.510 回答
1

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

所以你剩下的是一些策略,这会有所帮助,但不会让这个失败安全。

  1. 工作非常仔细和缓慢。尽可能多地使用 IDE 的功能,以减少人为错误的可能性。

  2. 成对工作。看着你肩膀的伙伴可能会发现你错过的故障。

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

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

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

于 2013-03-22T09:43:52.970 回答
0

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

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

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

于 2013-03-22T09:10:20.817 回答
0

不要重构测试套件。

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

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

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

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

于 2013-03-22T09:33:31.150 回答
0

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

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

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

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

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

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

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

于 2013-03-23T13:31:46.590 回答