Test Every Refactoring
"I think it is fair to say, that virtually there is no software that does not contain defects. However, we as software engineers still try to make as few errors as possible and we are also always looking for ways of improving the quality of what we deliver" [1].
Pattern Name: Test Every Refactoring
Problem
How do you avoid the introduction of errors that may break the system during refactoring?
Context
You are developing software and you want to make some refactorings before continuing coding. You followed TheFirstRefactoringStep, then you applied RefactoringInVerySmallSteps.
Forces
It is absolutely necessary that you test every refactoring. Unit Tests [2] are great for this. Remember that "the tests are your safety net, protecting you from breaking the system during fast evolution" [3].
Resulting Context
Testing systems every time that refactoring is applied will maintain a strict fence on the modified code sections. After each refactoring, if a problem was introduced, tests will alert immediately the causes and the conflicting location.
Rationale
"One way to improve software quality on the functional level is to have good tests in place" [1]. Besides, "work produced must be continuously validated through testing" [4]. Kent Beck and Erich Gamma expose in [5] their development methodology as "code a little, test a little (...)". Paraphrasing Beck and Gamma, refactoring should be guided by "refactor a little, test a little".
Refactoring introduces changes in the source code of the system, so after applying a refactoring, you should test the program to validate its functional integrity. If the tests fail after refactoring, it will be only necessary to verify the code sections involved in the process.
Origins
This pattern come from the WIKI pages about refactoring [6]. The original reduced pattern was: "You don't want to find out after 37 refactorings that somewhere around the 21st you made a mistake (...). Therefore, test every refactoring. Unit Tests are great for this (...)".
Acknowledgments
References
[1] Manfred Lange. It's Testing Time! Patterns for Testing Software. Proceedings of Sixth European Conference on Pattern Languages of Programs (EuroPLoP 2001). Irsee, Germany. July, 2001.
[2] Robert V. Binder. Testing Object-Oriented Systems. Addison -Wesley, 2000.
[3] Ron Jeffries, Ann Anderson, Chet Hendrickson. Extreme Programming Installed. Addison - Wesley, 2000.
[4] Ken Auer, Erik Meade, Gareth Reeves. The Rules of XP. Available at: http://www.rolemodelsoftware.com/moreaboutus/publications/rulesofxp.php. 2003. Last confirmed: April 13, 2004.
[5] Kent Beck, Erich Gamma. Test Infected: Programmers Love Writing Tests. Java Report, 3 (7). July, 1998.
[6] Wiki Pages About Refactoring. Available at: http://c2.com/cgi/wiki?WikiPagesAboutRefactoring. Last confirmed: June 26, 2004.
Author: SantiagoValdarrama
You applied RefactoringInVerySmallSteps. You don't want to find out after 37 refactorings that somewhere around the 21st you made a mistake. It is probably hard to go back without throwing all your changes away.
Therefore, test every refactoring. UnitTests are great for this. Also, if you added or moved code, make sure that it gets called. One way to do this is to introduce deliberate errors in the new code, so that the tests will fail. Another option is to set a breakpoint in the new code and see whether it is reached by the test code.
Run all the tests after the fewest possible edits - say 10 at the most. If you are not adding features, expect every test run to pass, and Undo your last edits if they don't.
That rule forces you to continuously return the code to a passing state. That passing state forces your designs to relentlessly decouple.
Also, of course, make sure you can roll back the refactoring without too much work in case the tests do fail. That is, at least for larger changes, have a backup copy of the state of your program before the refactoring (if your environment doesn't provide this automatically). --FalkBruegmann
This comment was transformed into BacktrackIfRefactoringFails pattern.
I usually first comment out the code to be removed, and only really delete it at the end of a RefactoringEpisode?. This only gives problems if the tests weren't good enough, the refactoring really broke something, and I find out too late. This approach works best in an environment where you have syntax highlighting, so that you can see immediately what is code and what isn't. --MarnixKlooster
I think this depends on your environment. Using ENVY, either Smalltalk or Java, I just delete the suspicious code. "Load Previous Edition" gives me a quick way back. --KentBeck
For example: I'm currently writing a large report-generating program that spits out CSV files. So when I refactor, I:
I guess the forces operating here are to do with the support of the VersionControl system your using for easy branch creation and 'polluting' the tree with dead branches, so you just need a good method for naming branches, and buy in from the rest of the team that small experimental branches are good.
--KevinWheatley?
This page mirrored in WikiPagesAboutRefactoring as of April 29, 2006