Refactoring Code and Tests

Let's recapitulate the definition of refactoring:

Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure.[11]

At first sight, this definition suggests that nothing will change in the tests either in the course of refactoring. This holds true for all those tests that view the behavior of the component under refactoring (CUR) from the outside, for example, all acceptance tests. In contrast, unit tests are normally a mixture of specification-based and implementation-dependent tests. For this reason, changes in the course of refactoring cannot be avoided. We can identify the following basic types and their impact on unit tests:

A common approach in refactoring is to first identify the test cases which should still be valid after refactoring. Next, you restructure in small steps and evaluate after each step whether some test is now superfluous or should be moved or expanded. The next action would always be the start of the remaining tests to validate your assumptions. If you think a refactoring task is complete, you once more swap your developer hat for your tester cap and reconsider all existing test cases with regard to adequacy and redundancy. Marick [00] emphasizes not trying to desperately keep all existing tests when doing system modifications. The more complex the tested scenario, the more difficult it will be to adapt it subsequently to the new situation. Such complicated tests should sometimes be thrown away and replaced by new ones for reasons of cost. The cost arising to adapt our test suites to the constantly changing program can be considerable, but it is seldom a waste of effort. One positive effect is that the developers are constantly confronted with their previous assumptions and get opportunities to improve things. After all, learning is a never ending process.

On the other hand, if we find that maintaining our test suites takes much more time than the actual modifications to the system, then this is a sign that we are writing the wrong tests. Here "wrong" means that we may be testing things that change too often, so that automated tests, such as testing private methods and attributes of a class, are of no use. [11][Fowler99, p. xvi]. [12]A few people suggested making the constant MIN_QUICKSORT public and using it to build the appropriate test data in the test case. This would indeed save us the labor of changing the test when changing the value, but it would (a) reveal an implementation detail and (b) require more complex test logic.