From Legacy Code to Testable Code #7 – Introduce Parameter
Today we’re going to talk about the Introduce Parameter refactoring pattern. This is one of the more useful patterns to clear code from dependencies.
Here’s a method we’d like to test:
public int Calculate(int x, int y)
{
if (OperationManager.ShouldAdd())
return x+y;
else
return x-y;
}
As we can see, there’s a nasty dependency right there in the middle. Since in this example the static ShouldAdd
call returns a Boolean result, we can turn this result into a parameter:
public int Calculate2(int x, int y, bool shouldAdd)
{
if (shouldAdd)
return x + y;
else
return x - y;
}
Thus removing the necessity to mock the static call, or handle side effects with the static calls (hello there, Mr. Singleton!).
Now, if the static call returns an object, the same thing applies.
public int Calculate(int x, int y)
{
if (OperationManager.Instance.ShouldAdd())
return x+y;
else
return x-y;
}
This time, we’ll transform the singleton instance into a parameter:
public int Calculate(int x, int y, OperationManager opManager)
{
if (opManager.ShouldAdd())
return x + y;
else
return x - y;
}
This case may not be as trivial as passing a Boolean value. Now we need to mock that object, which may be a problem, for instance (pun intended), if our OperationManager has a private constructor. In this case we can now introduce an interface we can mock:
public int Calculate(int x, int y, IOperationManager iopManager)
{
if (iopManager.ShouldAdd())
return x + y;
else
return x - y;
}
}
We still need to do all the modifications, but hopefully we have refactoring tools to help us.
Cleaning up dependencies not only helps us to test directly, but also makes it easy to extract the method into a new class.
Field extraction
An interesting case is when we have a field, being used inside the method:
Boolean shouldAdd;
public int Calculate(int x, int y)
{
if (shouldAdd)
return x+y;
else
return x-y;
}
That shouldAdd
field is a dependency. It may not look like it, but it really is. You will notice it, when you try to extract the method into a separate class.
When we need to get rid of field dependencies, we can use the same refactoring pattern. This is a transitional step on the road to extraction to a separate class.
public int Calculate(int x, int y, bool shouldAdd)
{
if (shouldAdd)
return x+y;
else
return x-y;
}
Note that while the parameter has the same name as the field, its scope has changed, and the method now uses it, rather than the field.
Reference: | From Legacy Code to Testable Code #7 – Introduce Parameter from our NCG partner Gil Zilberfeld at the Everyday Unit Testing blog. |