How does TDD make refactoring easier

refactoringtdd

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?

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.

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

Best Answer

One thing you need to keep in mind is that TDD is not mainly a testing strategy, but a design strategy. You write the tests first, because that helps you come up with a better decoupled design. And a better decoupled design is easier to refactor, too.

When you change the funcionality of a class or method, it's natural that the tests have to change, too. In fact, following TDD would mean that you change the tests first, of course. If you have to change a lot of tests to just change a single bit of functionality, that typically means that most tests are overspecifying the behavior - they are testing more than they should test. Another problem could be that a responsibility isn't well encapsulated in your production code.

Whatever it is, when you experience many tests failing because of a small change, you should refactor your code so that it doesn't happen again in the future. It's always possible to do that, though not always obvious how to.

With bigger design changes, things can become a bit more complicated. Yes, sometimes it will be easier to write new tests and discard the old ones. Sometimes, you can at least write some integration tests that test the whole part that gets refactored. And you hopefully still have your suite of acceptance tests, that are mostly unaffected.

I haven't read it yet, but I have heard good things about the book "XUnit Test Patterns - Refactoring Test Code".

Related Topic