refactoring test codectchen/pdf/rtc.pdf · why refactoring test code the ideal test code /...
TRANSCRIPT
Reference
Arie van Deursen et al., “RefactoringTest Code, ” XP 2001 Conference, 2001.
Outline
IntroductionTest Code SmellsRefactoringsConclusion
Introduction
Why Refactoring Test Code
The ideal test code / production code ratio approaches 1:1, it is not surprising that unit tests are begin refactored.Refactoring test code is different from refactoring production code:
There is a distinct set of bad smells.Improving test code involves additional test-specific refactorings.
Unit Testing
Unit testing is a technique at the heart of extreme programming (XP).A typical test for a particular method:
1. Code to set up the fixture.2. The call of the method.3. A comparison of the actual results with the.
expected values.4. Code to tear down the fixture.
Tools support- xUnit.
Why so many tests?
It helps the developers to overcome their fear for change:
You don’t break anything.
Problem:Changes in functionality will typically involve changes in the test code as well.
Test Code & Production Code
TestCode
ProductionCode
verify
TestCode’
ProductionCode’
trace
functionality changed
Refactoring
Change the code without change the functionality of the system.How to do?
A catalog of code smells.A wide range of refacorings.
Refactoring test code:Test smells.Test refactorings.
Test Code Smells
Smell 1: Mystery Guest
A test is no longer self contained if it uses external resources:
There is not enough information to understand the tested functionality, making it hard to use it as documentation.Using external resources introduces hidden dependencies.
Example: Solution:
Inline Resource (1), Setup External Resource (2)
Smell 2: Resource Optimism
Nondeterministic behavior may be occurred if a test code makes optimistic assumptions about the existence (or absence) and state of external resource.
Tests run fine at one time and fail miserably the other time.
Example: Solution:
Setup External Resource (2)
Smell 3: Test Run War
Tests run fine as long as you are the only one testing but fail when more programmers run them.
This is most likely caused by resource interference.
Example:Solution:
Make Resource Unique (3)
Smell 4: General Fixture
The setUp fixture is too general and different tests only access part of the fixture.
It is harder to read and understand.It may make tests run more slowly.
Example:Solution:
Extract Method (F:110) with Inline Method (F:117), Extract Class (F:149).
Smell 5: Eager Test
A test method checks several methods of the object to be tested
It is hard to read and understand, and therefore more difficult to use as documentation.It makes tests more dependent on each other and harder to maintain.
Example:Solution:
Extract Method (F:110)
Smell 6: Lazy Test
Several test methods check the same method using the same fixture.
For example, check the values of different instance variables.
Solution:Inline Method (F:117)
Smell 7: Assertion Roulette
Having a number of assertions in a test method that have no explanation.
If one of the assertions fails, you do not know which one it is.
Example:Solution:
Add Assertion Explanation (5)
Smell 8: Indirect Testing
A test class contains methods that actually perform tests on other objects.Solution:
Extract Method (F:110) & Move Method (F:142).
Smell 9: For Testers Only
A production class contains methods that are only used by test methods, these methods either:
Are not needed and can be removed, orAre only needed to set up a fixture for testing.
Solution:Extract Subclass (F:330)
Smell 10: Sensitive Equality
Writing equality checks by using the toString method.
It may depend on many irrelevant details, such as commas, quotes, spaces, etc.
Solution:Introduce Equality Method (6)
Smell 11: Test Code Duplication
Test code may contain undesirable duplication.
Duplication in the same test class.Duplication across test classes.
Solution:Extract Method (F:110).
Refactorings
Refactoring 1: Inline Resource (1/2)
Incorporating the resource in the test code to remove the dependency between a test method and some external resource.
Setting up a fixture in the test code that holds the same contents as the resource.A example of this is putting the contexts of a file that is used into some string in the test code.
Refactoring 1: Inline Resource (2/2)
If the contents of the resource are large:Suffering from Eager Test (5).Using Extract Method (F:110) or Reduce Date (4).
Refactoring 2: Setup External Resource
If it is necessary for a test to rely on external resource, such as directories, databases, or files:
Explicitly creates or allocates these resources before testing.Releases them when done.
Refactoring 3: Make Resource Unique
A lot of problems originate from the use of overlapping resource name:
Using unique identifiers for all resources that are allocated, for example:
Time-stamp.GUID.
Refactoring 4: Reduce Data
Minimize the data that is setup in fixtures to the bare essentials.
It make them better suitable as documentation.Your tests will be less sensitive to changes.
Refactoring 5: Add Assertion Explanation
Assertions in the JUnit have an optional first argument to give an explanatory message to the user when the assertion fails.
Using this message to distinguish between different assertions that occur in the same test.
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.Then rewrite the tests that use string equality to use the “equals” method.
Related Work
Martin Fowler:Focuses on production code.Unit tests is to prove that a refactoring did not break anything.Unit test are used as documentation of the production code.
Schneider:Describes how to prevent these smells right from the start by discussing a number of best practices for writing tests with JUnit.
Conclusion
While working on the XP project, the authors observed that the quality of the test code was not as high as the production code.An open question: how test code refactoring interacts with the other XP practices?The precise interplay between test refactoring and the XP practices is a subject of the authors’ further research.
Q&A
Example- Smell 1: Mystery Guest
external resource
Example- Smell 2: Resource Optimism
Do we have this file?
Example- Smell 3: Test Run War
files may already exist
Example- Smell 4: General Fixture
Example- Smell 5: Eager Test
2
1
Example- Smell 7: Assertion Roulette
Example- Refactoring 1: Inline Resource
Example- Refactoring 2: Setup External Resource
explicitly creates resources
release them when done
Example- Refactoring 3: Make Resource Unique
or
Example- Refactoring (F:110): Extract Method
Example- Refactoring (F:110): Extract Method
Example- Refactoring 5: Add Assertion Explanation