You didn’t think we’ve covered everything, did you? Here’s a short reminder of what we got through:
The Legacy Code To Testable Code Series
General patterns | Accessibility | Dealing with dependencies | Advanced patterns |
---|---|---|---|
Introduction | Add setters | Extract method | Static constructors (initializers) |
Renaming | More accessors | Extract class | More static constructors |
Add overload | Introduce parameter | Instance constructors | |
Testable object | Conditionals to guard blocks |
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.
0 Comments