Refactorings


Bad smells seem to arise more often in production code than in test code. The main reason for this is that production code is adapted and refactored more frequently, allowing these smells to escape.

One should not, however, underestimate the importance of having fresh test code. Especially when new programmers are added to the team or when complex refactorings need to be performed, clear test code is invaluable. To maintain this freshness, test code also needs to be refactored. We define test refactorings as changes (transformations) of test code that do not add or remove test cases, and make test code more understandable, readable, and maintainable.

The production code can be used as a (simple) test case for the refactoring: If a test for a piece of code succeeds before the test refactoring, it should also succeed after the refactoring (and no, replacing all test code by assert(true) is not considered a valid refactoring). This obviously also means that you should not modify production code while refactoring test code (similar to not changing tests when refactoring production code).

While working on our test code, we encountered the following refactorings.

Refactoring 1: Inline Resource

To remove the dependency between a test method and some external resource, we incorporate that resource into the test code. This is done by setting up a fixture in the test code that holds the same contents as the resource. This fixture is then used instead of the resource to run the test. A simple example of this refactoring is putting the contents of a file that is used into some string in the test code.

If the contents of the resource are large, chances are high that you are also suffering from the Eager Test (5) smell. Consider conducting Extract Method (F:110) or Reduce Data (4) refactorings.

Refactoring 2: Setup External Resource

If it is necessary for a test to rely on external resources, such as directories, databases, or files, make sure the test that uses them explicitly creates or allocates these resources before testing and releases them when done (take precautions to ensure the resource is also released when tests fail).

Refactoring 3: Make Resource Unique

A lot of problems originate from the use of overlapping resource names, either between different test runs done by the same user or between simultaneous test runs done by different users. Such problems can easily be prevented (or repaired) by using unique identifiers for all resources that are allocated for example, by including a timestamp. When you also include the name of the test responsible for allocating the resource in this identifier, you will have fewer problems finding tests that do not properly release their resources.

Refactoring 4: Reduce Data

Minimize the data that is set up in fixtures to the bare essentials. This offers two advantages: (1) It makes them more suitable as documentation, and (2) your tests will be less sensitive to changes.

Refactoring 5: Add Assertion Explanation

Assertions in the JUnit framework have an optional first argument to give an explanatory message to the user when the assertion fails. Testing becomes much easier when you use this message to distinguish between different assertions that occur in the same test. Maybe this argument should not have been optional.

Refactoring 6: Introduce Equality Method

If an object structure needs to be checked for equality in tests, add an implementation for the equals method for the object's class. You then can rewrite the tests that use string equality to use this method. If an expected test value is represented only as a string, explicitly construct an object containing the expected value, and use the new equals method to compare it to the actually computed object.



Extreme Programming Perspectives
Extreme Programming Perspectives
ISBN: 0201770059
EAN: 2147483647
Year: 2005
Pages: 445

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net