【发布时间】:2008-10-07 15:21:32
【问题描述】:
Martin Fowler 说我们应该在添加新功能之前进行重构(鉴于原始程序结构不完善)。
所以我们都想重构这个 dirty 代码库,这是肯定的。我们还知道,如果没有单元测试代码,很容易引入细微的错误。
但这是一个庞大的代码库。为其添加一整套测试似乎是不切实际的。
在这种情况下你会怎么做?
【问题讨论】:
标签: unit-testing testing refactoring
Martin Fowler 说我们应该在添加新功能之前进行重构(鉴于原始程序结构不完善)。
所以我们都想重构这个 dirty 代码库,这是肯定的。我们还知道,如果没有单元测试代码,很容易引入细微的错误。
但这是一个庞大的代码库。为其添加一整套测试似乎是不切实际的。
在这种情况下你会怎么做?
【问题讨论】:
标签: unit-testing testing refactoring
我的建议是你尽可能少地触摸并添加你需要的东西。我发现最好不要管它,尤其是在你的最后期限很紧的时候。
如果您进行了单元测试,那将是另一回事,但是当您更改代码时,就像触摸蜘蛛网一样。改变一件事会影响其他一切。
【讨论】:
让我推荐 Michael Feathers 的书Working effectively with legacy code。它包含许多现实的例子,并展示了处理遗留代码野兽的好技术。
【讨论】:
将单元测试添加到您要重构的代码中。
【讨论】:
请参阅Rands in Repose's Trickle Theory,了解他遇到的具有大量错误的类似问题。他的建议:
我的建议是:开始
【讨论】:
当单元测试不存在或很难在给定的时间范围内添加适当的覆盖范围时,您将不得不依赖定期测试。从理论上讲,你所说的这个庞大而肮脏的代码库已经被认为适合使用一段时间了。为了做出决定,要么进行程序员测试,要么进行 QA 测试,要么进行某种由客户进行的测试。你想知道它是如何完成的,做了什么,是否足以涵盖你将要做的改变,然后承诺再次完成,加上新代码所需的任何测试,直到产品好够了。
单元测试对程序员来说是一项很棒的服务,但它们并不是唯一的测试。
【讨论】:
不要将其视为一个非此即彼/或的命题。通过添加单元测试可以增加价值——您不必添加整个套件即可获得添加一些测试的好处。
【讨论】:
我发现自己经常遇到这种情况,因为这几乎是我过去两年一直坚持的问题。
正确的方法实际上更多地取决于社会和组织方面,而不是技术方面。您的工作是为您的组织创造价值。如果重构产生的价值大于成本,那么你应该能够出售它。就我而言,关键因素包括:
相关项目的预期所有权。 如果您希望在可预见的未来成为这个特定软件的重要利益相关者,那么这是一个支持对糟糕的代码库 b/c 进行更广泛修改的论据,随着您继续维护它,它将获得更多回报。如果您要添加免下车功能,请采用更加不干涉的方法。
所做更改的复杂性。 如果您正在对代码库进行非常复杂的更改(“脏”代码库中的典型情况,b/c 此类源通常是紧密耦合且不连贯的),则更有可能需要进行一些重构。此类更改也不是作为代码忍者的结果,因为它们对于您简单地推理您正在进行的更改是必要的。这也与您正在修改的代码库的“坏处”有关。几乎不可能为紧密耦合、不连贯的混乱创建最简单的单元测试。 (我根据经验说话。我几乎无法维护的项目之一是大约 20k 行,不包括生成的代码,在 12 个文件中。整个应用程序是一个名为“Form1”的类。这是开发人员滥用部分类特征。)
组织监督。 您的组织监督的强度和严格性在这里发挥作用。如果您的团队确实执行了一些核心最佳实践,例如代码审查,并且不只是口头上说,我更倾向于不进行广泛的重构。价值权衡可能更倾向于尽可能少地触摸,因为您有另一双新的眼睛检查以确保您所做的少数更改都没有不良副作用。同样,更严格的监督更有可能不赞成“游击”代码策略,即进行更改请求中没有严格要求的更改。
你的老板。如果你的老板站在你这边,你就更有可能对你的代码库进行长期的价值改进,特别是如果你能证明这一点现在就预算小时数而言,现在增加的成本在路上。请记住,您的经理比您更了解该软件在大局中的作用。如果它是一个只有十或二十人使用的软件,它就不需要像一个十或两万人使用的软件所要求的那种长期维护改进。
在考虑此类时间投资时,您需要回答的核心问题是“价值在哪里?”那么你需要追逐那个价值。
【讨论】:
首先,为您要更改的部分添加测试。其次,重构代码,直到它对理智的人有意义。第三,进行必要的更改。
【讨论】:
很大程度上取决于您的语言。如果您使用的是静态类型语言,则可能无需单元测试就可以逃脱。我从事过许多与您所描述的完全一样的工作。
如果工作量很大(比如说,一个人一年或 3 个人 4 个月),那么您可能希望有人拆开代码并首先对其进行分析。
如果它是一种动态语言,问题会更大——您需要进行一定程度的单元测试。也许您可以将单元测试添加到您需要接触的领域。
我只区分静态语言和动态语言,因为在静态类型语言中进行重构要容易得多——它们往往更容易预测。我并不讨厌 Ruby 或其他任何东西——我也在 ROR 上花了一年的时间。我只是认为他们需要不同的方法。
【讨论】:
Fowler 还建议您永远不要在没有测试安全的情况下进行重构。但是,您如何进行这些测试呢?还有,你能走多远?
之前推荐的书(Michael Feathers 的Working Effectively with Legacy Code)是该主题的权威著作。
需要快速阅读?看看迈克尔早先的同名article (PDF)。
【讨论】:
你必须从某个地方开始。您可以从在可能的地方添加测试开始,并随着您的前进而构建您的测试套件。即使你有一些“愚蠢”的测试,它们什么都不做,只是告诉你你只是能够测试代码的某些部分告诉你一些事情。
【讨论】:
如果添加一套测试是不切实际的,那么代码库就存在严重问题。
添加新代码并希望它只是工作是不好的。
编写测试、重构基础并添加新代码。如果你不能测试它,你永远不会知道它是否正确。
【讨论】:
尽可能少地触摸。并祈祷。
【讨论】:
我同意其他人的观点,尽量少碰。 但是,您也可以随时开始为您将要接触的新功能和/或现有功能编写单元测试。没有人说你需要编写单元测试来测试 100% 的现有代码库。
【讨论】: