避免 TDD 使大规模重构变得更加困难

4

我在TDD方面仍然是一个相对初学者,经常陷入这样的困境:在尝试添加新功能时,我会发现自己在某个地方设计得不够好。

大多数情况下,这意味着从前10个需求中增长出来的API,在添加下一个需求时无法扩展。我意识到我必须对现有功能进行大规模重新设计,包括结构,以便以一种良好的方式添加新内容。

这很好,但在这种情况下,API将会改变,因此所有最初的测试都必须更改。这通常比仅重命名方法要复杂得多。

我想我的问题有两个方面:首先,我应该如何避免一开始就陷入这种情况?其次,如果我确实遇到了这种情况,重构测试并允许使用新API增长新功能的安全模式是什么?

编辑:有很多很棒的答案,我会尝试几种技术。我认为最有帮助的答案被标记为解决方案。


显然,这个问题也被称为TDD的僵局问题,Uncle Bob在《转换优先原则》中提到过这个问题。 - tddtrying
6个回答

6
我应该如何避免一开始就陷入这种境地?
最普遍的规则是:编写测试时要考虑到这样的重构。特别是:
- 测试应使用帮助方法来构造任何API特定的内容(例如示例对象)。这样,如果构造发生更改(例如在构造对象中添加必填字段后),您只需要更改一个地方。 - 检查API输出也是如此。 - 测试应构建为“与默认值不同”,默认值由上述提供。例如,如果您的测试检查方法对字段x的影响,则应仅在测试中设置x字段,并从默认值中获取其他字段。
实际上,这些规则适用于一般代码。
安全的重构测试和允许新的API功能增长的模式是什么?
每当您发现API更改使您在多个位置更改测试时,请尝试找出如何将代码移动到单个位置。这遵循了上述原则。

2

测试代码要小而精。好的测试用例只会调用测试主体中的1-3个方法并对结果进行一些断言。当这三个方法之一发生更改时,这些测试才需要更改。

使你的测试代码整洁。如果您还没有阅读Robert C. Martin的《Clean Code》,请务必阅读并将其规则应用于您的生产代码和测试代码。这有可能减少任何重构所影响的表面积。

经常进行重构。不要(可能是无意识地)等待直到你必须进行大量重构,而是经常进行小规模的重构。

如果你面临大规模的重构,请将它分解成几个(或者如果必要,成百上千个)小的重构过程。


1

针对这种情况,我建议您锁定功能并使迭代时间短。

由于迭代时间短,功能将被分组为较小、隔离的组。这减少了需要考虑一些伟大设计的必要性,而这些设计可能不适应用户的需求。本周的代码只能与本周的代码配合使用。这降低了新功能混淆旧功能的机会。


0
以下可能会有所帮助:
  • 遵循良好的编码标准和实践,包括 TDD 和利用设计模式来生成结构良好的设计。整个应用程序因此应更容易扩展以包含新功能。
  • 良好的关注点分离。如果你的 API 能够与其他功能(计算、数据库访问等)良好地分离,那么在不改变 API 或者反之的情况下更改功能应该会更容易。
  • 使用 BDD 在较高级别提供自动化测试(比如更像用户测试而不是单元测试)。这些测试应该能够在重构时给你信心,即使所有的单元测试都因为重构而失败了。
  • 使用 Dependency Injection 容器,比如 Windsor。通过在类依赖项上抽象化,当这些依赖项发生变化时,相比将它们编码到你的类中,这样做会产生更少的重新工作(特别是如果你有很多单元测试)。

0

是的,这是TDD难以避免的问题,因为它的整个理念就是避免由于事先进行大规模设计而导致的过度工程化。因此,在TDD中,您的设计会经常发生变化。测试用例越多,每次重构所需的工作量就越大,这实际上会阻碍重构,与TDD的整个理念相悖。

尽管您的设计会发生变化,但您的基本要求应该是相当稳定的,因此,在“高层次”上,您的应用程序工作方式不应该发生太大变化。这就是为什么我建议将所有测试放在“高层次”(集成测试)上。低级别测试是一种负担,因为您必须在重构时更改它们。高层次测试需要更多的工作,但我认为最终值得。

我一年前写了一篇关于这个问题的文章:http://www.hardcoded.net/articles/high-level-testing.htm


0

在TDD中,你不应该让自己“陷入困境”。第11个测试不应该如此严重地改变设计,以至于前面的10个测试都需要更改。仔细思考为什么会有这么多测试需要更改 - 逐一详细查看,并看看是否可以想出一种方法,在不破坏现有测试的情况下进行更改。

例如,如果您需要向所有调用某个方法的方法添加参数:

  • 您可以保留现有的参数较少的方法,并将其委派给新方法并添加默认参数;
  • 您可以让所有测试调用实用程序方法(或者可能是安装方法),该方法调用旧方法,因此只需要在一个地方更改方法调用;
  • 您可以让您的IDE使用单个命令完成所有更改。

TDD和重构彼此协作;每个都有助于另一个。因为您从TDD中获得了全面的单元测试,所以重构是安全的;因为您拥有组织、智力和编辑工具来自由地重构,所以您可以使测试与设计保持良好的同步。您说您是TDD的初学者;也许您需要在学习TDD的同时培养自己的重构技能。


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