07-12-2012, 04:54 PM
Refactoring Test Code
Refactoring Test Code.PDF (Size: 34.8 KB / Downloads: 24)
ABSTRACT
Two key aspects of extreme programming (XP) are unit
testing and merciless refactoring. Given the fact that the
ideal test code / production code ratio approaches 1:1, it is
not surprising that unit tests are being refactored. We found
that refactoring test code is different from refactoring production
code in two ways: (1) there is a distinct set of bad
smells involved, and (2) improving test code involves additional
test-specific refactorings. To share our experiences
with other XP practitioners, we describe a set of bad smells
that indicate trouble in test code, and a collection of test
refactorings to remove these smells.
INTRODUCTION
“If there is a technique at the heart of extreme programming
(XP), it is unit testing” [1]. As part of their programming
activity, XP developers write and maintain (white
box) unit tests continually. These tests are automated,
written in the same programming language as the production
code, considered an explicit part of the code, and put
under revision control.
The XP process encourages writing a test class for every
class in the system. Methods in these test classes are used
to verify complicated functionality and unusual circumstances.
Moreover, they are used to document code by explicitly
indicating what the expected results of a method
should be for typical cases. Last but not least, tests are
added upon receiving a bug report to check for the bug and
to check the bug fix [2]. A typical test for a particular
method includes: (1) code to set up the fixture (the data
used for testing), (2) the call of the method, (3) a comparison
of the actual results with the expected values, and (4)
code to tear down the fixture. Writing tests is usually supported
by frameworks such as JUnit [3].
TEST CODE SMELLS
This section gives a overview of bad code smells that are
specific for test code.
Smell 1: Mystery Guest.
When a test uses external resources, such as a file containing
test data, the test is no longer self contained. Consequently,
there is not enough information to understand the
tested functionality, making it hard to use that test as
documentation.
Moreover, using external resources introduces hidden dependencies:
if some force changes or deletes such a resource,
tests start failing. Chances for this increase when
more tests use the same resource. The use of external resources
can be eliminated using the refactoring Inline Resource
(1). If external resources are needed, you can apply
Setup External Resource (2) to remove hidden dependencies.
RELATED WORK
Fowler [5] presents a large set of bad smells and refactorings
that can be used to remove them. The difference between
his work and ours is that we focus on smells and
refactorings that are typical for test code whereas his book
focuses more on production code. The role of unit tests in
[5] is also more geared towards proving that a refactoring
didn’t break anything than to be used as documentation of
the production code.
Instead of focusing on cleaning test code which already has
bad smells, Schneider [6] describes how to prevent these
smells right from the start by discussing a number of best
practices for writing tests with JUnit.
The C2 Wiki contains some discussion on the decay of unit
test quality and practice as time proceeds2, and on the
maintenance of broken unit tests3. Opinions vary between
repairing broken unit tests, deleting them completely, and
moving them to another class in order to make them less
exposed to changes (which may lead to our Indirect Testing
(8) smell).
CONCLUSIONS
In this paper, we have looked at test code from the perspective
of refactoring. While working on our XP project,
we observed that the quality of the test code was not as
high as the production code. Test code was not refactored
as mercilessly as our production code, following Fowler’s
advice that it is okay to copy and edit test code, trusting our
ability to refactor out truly common items later [5, p. 102].
When at a later stage we started to refactor test code more
intensively, we discovered that test code has its own set of
problems (which we translated into smells) as well as its
own repertoire of solutions (which we formulated as test
refactorings).