TDD 如何使重构更容易? [英] How does TDD make refactoring easier?

查看:54
本文介绍了TDD 如何使重构更容易?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我听说使用 TDD 开发的项目更容易重构,因为这种做法会产生一组全面的单元测试,如果任何更改破坏了代码,这些单元测试(希望)将失败.然而,我见过的所有示例都涉及重构实现——例如,用更有效的算法更改算法.

I've heard that projects developed using TDD are easier to refactor because the practice yields a comprehensive set of unit tests, which will (hopefully) fail if any change has broken the code. All of the examples I've seen of this, however, deal with refactoring implementation - changing an algorithm with a more efficient one, for example.

我发现重构架构在设计仍在制定的早期阶段更为常见.接口发生变化,添加了新类 &删除,即使是函数的行为也可能略有改变(我以为我需要它来做这个,但它实际上需要这样做)等等......但是如果每个测试用例都与这些不稳定的类紧密耦合,就不会每次更改设计时,您都必须不断地重写测试用例吗?

I find that refactoring architecture is a lot more common in the early stages where the design is still being worked out. Interfaces change, new classes are added & deleted, even the behavior of a function could change slightly (I thought I needed it to do this, but it actually needs to do that), etc... But if each test case is tightly coupled to these unstable classes, wouldn't you have to be constantly rewriting your test cases each time you change a design?

在 TDD 中什么情况下可以更改和删除测试用例?您如何确定更改测试用例不会破坏它们?此外,似乎必须将综合测试套件与不断变化的代码同步会很痛苦.我知道单元测试套件可以在维护期间提供巨大帮助,一旦软件构建、稳定和运行,但这是游戏后期,因为 TDD 也应该在早期提供帮助.

Under what situations in TDD is it okay to alter and delete test cases? How can you be sure that altering the test cases don't break them? Plus it seems that having to synchronize a comprehensive test suite with constantly changing code would be a pain. I understand that the unit test suite could help tremendously during maintenance, once the software is built, stable, and functioning, but that's late in the game wheras TDD is supposed to help early on as well.

最后,一本关于 TDD 和/或重构的好书能解决这些问题吗?如果是这样,您会推荐哪个?

Lastly, would a good book on TDD and/or refactoring address these sort of issues? If so, which would you recommend?

推荐答案

另外,似乎必须同步一个全面的测试套件不断变化的代码将是一种痛苦.我了解单位测试套件可以提供极大帮助在维护期间,一旦软件已建成、稳定且正常运行,但这是游戏后期,而 TDD 是也应该尽早提供帮助.

Plus it seems that having to synchronize a comprehensive test suite with constantly changing code would be a pain. I understand that the unit test suite could help tremendously during maintenance, once the software is built, stable, and functioning, but that's late in the game wheras TDD is supposed to help early on as well.

我确实同意,在这些早期的变化中,当发生重大的架构变化时,可以感受到单元测试套件的开销,但我的观点是,单元测试的好处远远超过了这个缺点.我认为这个问题常常是心理问题——我们倾向于将我们的单元测试视为代码库的二等公民,我们讨厌不得不与他们混为一谈.但随着时间的推移,随着我开始依赖它们并欣赏它们的用处,我开始认为它们与代码库的任何其他部分一样重要,同样值得维护和工作.

I do agree that the overhead of having a unit test suite in place can be felt at these early changes, when major architectural changes are taking place, but my opinion is that the benefits of having unit tests far outweigh this drawback. I think too often the problem is a mental one - we tend to think of our unit tests as second class citizens of the code base, and we resent having to mess with them. But over time, as I've come to depend on them and appreciate their usefulness, I've come to think of them as no less important and no less worthy of maintenance and work as any other part of the code base.

主要的架构变化"真的只是重构吗?如果您只是重构,无论多么戏剧化,并且测试开始失败,这可能表明您无意中更改了某处的功能.这正是单元测试应该帮助您捕获的内容.如果您同时对功能和架构进行彻底的更改,您可能需要考虑放慢速度并进入红色/绿色/重构槽:没有新的(或更改的)功能没有额外的测试,并且没有更改重构时的功能(和破坏性测试).

Are the major architecural "changes" taking place truly only refactorings? If you are only refactoring, however dramatically, and tests begin to fail, that may tell you that you've inadvertantly changed functionality somewhere. Which is just what unit tests are supposed to help you catch. If you are making sweeping changes to functionality and architecture at the same time, you may want to consider slowing down and getting into that red/green/refactor groove: no new (or changed) functionality w/o additional tests, and no changes to functionality (and breaking tests) while refactoring.

更新(基于评论):

@Cybis 对我的主张提出了一个有趣的反对意见,即重构不应该破坏测试,因为重构不应该改变行为.他的反对意见是重构确实改变了 API,因此测试中断".

@Cybis has raised an interesting objection to my claim that refactoring shouldn't break tests because refactoring shouldn't change behavior. His objection is that refactoring does change the API, and therefore tests "break".

首先,我鼓励任何人访问有关重构的规范参考:Martin Fowler 的 bliki.刚才我回顾了它,有几件事让我印象深刻:

First, I would encourage anyone to visit the canonical reference on refactoring: Martin Fowler's bliki. Just now I reviewed it and a couple things jump out at me:

  • 正在更改界面重构? Martin 指的是重构为行为保持"变化,其中意味着当接口/API 发生变化时那么所有的来电者接口/API 也必须改变.我说,包括测试.
  • 这并不意味着行为发生了变化.福勒再次强调,他的重构的定义是变化是行为保存.
  • Is changing an interface refactoring? Martin refers to refactoring as a "behavior-preserving" change, which means when the interface/API changes then all callers of that interface/API must change as well. Including tests, I say.
  • That does not mean that the behavior has changed. Again, Fowler emphasizes that his definition of refactoring is that the changes are behavior preserving.

有鉴于此,如果在重构期间必须更改一个或多个测试,我不认为这是破坏"测试.它只是重构的一部分,保留整个代码库的行为.我认为必须更改的测试与作为重构的一部分必须更改的代码库的任何其他部分之间没有区别.(这可以追溯到我之前所说的,将测试视为代码库的一等公民.)

In light of this, if a test or tests has to change during a refactoring, I don't see this as "breaking" the test(s). It's simply part of the refactoring, of preserving the behavior of the entire code base. I see no difference between a test having to change and any other part of the code base having to change as part of a refactoring. (This goes back to what I said before about considering tests to be first-class citizens of the code base.)

此外,我希望测试,甚至修改后的测试,在重构完成后继续通过.无论该测试正在测试什么(可能是该测试中的断言)在重构完成后都应该仍然有效.否则,这是一个危险信号,表明在重构期间行为以某种方式发生了改变/倒退.

Additionally, I would expect the tests, even the modified tests, to continue to pass once the refactoring is done. Whatever that test was testing (probably the assert(s) in that test) should still be valid after a refactoring is done. Otherwise, that's a red flag that behavior changed/regressed somehow during the refactoring.

也许这种说法听起来像废话,但想想看:我们没有考虑在生产代码库中移动代码块并期望它们继续在它们的新上下文中工作(新类、新方法签名等).我对测试也有同样的感觉:也许重构改变了测试必须调用的 API,或者测试必须使用的类,但最终测试的重点不应该因为重构而改变.

Maybe that claim sounds like nonsense but think about it: we think nothing about moving blocks of code around in the production code base and expecting them to continue to work in their new context (new class, new method signature, whatever). I feel the same way about a test: perhaps a refactoring changes the API that a test must call, or a class that a test must use, but in the end the point of the test should not change because of a refactoring.

(我能想到的唯一例外是测试低级实现细节的测试,您可能希望在重构期间更改这些细节,例如用 ArrayList 或其他东西替换 LinkedList.但在这种情况下,人们可能会争论测试过度测试,过于僵化和脆弱.)

(The only exception I can think of to this is tests that test low-level implementation details that you may want to change during a refactoring, such as replacing a LinkedList with an ArrayList or something. But in that case one could argue that the tests are over-testing and are too rigid and fragile.)

这篇关于TDD 如何使重构更容易?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆