Refactoring is improving code in a way that doesn't affect it's functionality. That is, the public interface stays the same, but you might improve code formatting, algorithms, or implementation details. In a few cases, this might even be cleaning up a class hierarchy.
Because the behaviour of the code is the same before and after refactoring, you can use the same unit tests without change during refactoring. If you do not have tests, then writing tests for the part you are going to work on is the first step of refactoring:
- Write unit tests for the unit you're working on.
- Assert that the tests succeed with the current implementation.
- Refactor.
- Assert that the tests succeed with the new implementation.
You may have tests that don't test the externally visible behaviour, but test implementation details. Such tests make it easier to locate a problem, but they will have to be changed during step 3 “refactor”. The workflow is:
- Design the refactoring. I.e. think about what you're going to change, and how it should work afterwards.
- Rewrite the tests to test for the new implementation details.
- Assert that the tests fail with the current implementation.
- Refactor.
- Assert that the tests succeed with the new implementation.
If you have tests for implementation details, it is crucial to clearly separate these from tests that test the public interface.
This workflow guarantees that during a refactoring step you will not forget a required piece of functionality.
How would you mark regions of code that require refactoring? I recommend using a comment as TODO: REFACTOR because explanation
. Such comments could be added during a code review. You can easily grep
for such a comment in a code base. You suggested editing in a throw new RefactorException()
– but that means …
- … you can't use the software until the refactoring is performed. Shipping working code now can be better than shipping beautiful code tomorrow.
- … you can't run tests while those exceptions are in the code.
Test-driven refactoring will also lead you to the areas that need improvement, but contrary to your exceptions will give you the certainty that the code behaves the same before and after.