Refactoring to Allow Test Doubles
Sometimes, when you make a class, it directly instantiates an object to use in its methods. For example:
public void myFunc()
{
MyType object = new MyType();
object.doSomething();
this.thingy = object.getSomething();
}This is generally viewed as bad, since youâre tightly coupling your class to the instantiated one. âBut,â you say, âI know that I will never use any other class, and I certainly donât want users of the class to be supplying alternatives to use.â These concerns are mostly valid, but they leave out a very important thing to pay attention to: testing.
You should be able to replace that object with a test double (often mistakenly called mocks; mocks are a specific type of double), for all the usual reasons for having doubles. But, as is, you canât replace it. Doing so would allow all users of my class to do it too, wouldnât it?
What if I told you that you could have your cake and eat it too? (such a strange phraseâĶ) Well, you can, and Iâll give you a nutshell explanation about how, then go into the details.
Keep in mind, it doesnât require any sort of annotations or DI frameworks (Iâm not a big fan of trusting âmagicâ to do what I should be doing; I have nothing against those who do, but I just donât like doing it).
The Nutshell
Assuming youâre doing your tests the typical way of having them in the same package as what theyâre testing but in a different directory, you can actually very easily set something up to replace your âunchangeableâ object with a test double. All you need is a package-private (default access) way to supply the class with the double and a private way to access it. Then you can use the package-private calls in your tests to supply the class with the test double.
There are a few ways to do each part of it, so Iâll dig into that.
Suppliers
I can think of three different ways to tell the object being tested what double to use, and it turns out that theyâre exactly the same three basic ways of doing typical dependency injection:
- Constructor
- Setter (aka Mutator)
- Field
You may notice that I used numbers instead of bullet points. This is because I consider some to be superior to others. You should consider each method in the order that they appear.
Constructor Injection
Make a package-private constructor on your class that accepts the normal parameters, plus the test double. Then your existing constructor(s) and/or static factory method(s) can delegate to that constructor with the default object.
This is often the best option, especially for immutable types. By passing it in through the constructor, the object will not need to change, which immutable types arenât supposed to do. Granted, passing it in later doesnât break the classâ effective immutability, since the changes you are applying arenât possible in the production code, but it removes the âawesome side benefitâ that I mention at the end of the article.
Using the constructor also reduces the number of lines required in your tests, since it is a line that youâre already going to be typing.
The only real downside to constructor injection is that it doesnât allow the ability to change out the test double without creating a new object. This is rarely a problem, though, since you usually donât need more than one per test, and each test should be creating a new instance to ensure test independence.
Setter Injection
Make a package-private setter method that the test can call to set a field to the test double. The constructor sets the field to a default or null (the technique for accessing the test double can test for null and use the default if it is).
This isnât a horrible option, but, as stated above, is definitely not ideal for immutable classes. The best part of it is its obviousness. Seeing the setter being called makes it clearer about what the test double is doing there, but not usually a lot clearer. Its other benefit, as the previous section states, is the ability to change the double out without creating a new instance of this class. Again, this isnât all that helpful, really.
Field Injection
This is done by making the field that stores the double package-private and directly assigning the double to it.
You pretty much shouldnât do this, for the same reasons you donât make fields public. You may think that itâs harmless to do this, since itâs only for testing, but it removes the âawesome side benefitâ at the end of the article.
Technically, itâs an option that works, but thatâs the only reason I even bothered to include it in the article. Itâs bad design; avoid it at all costs. Please.
Accessors
The accessors are almost always private. This is usually because we didnât even want them until we started to refactor our code to use the test doubles. I can only think of two types of accessors, which are mirrors of the suppliers. Since you canât do a mirror of the constructor, there are only two:
- Getter
- Field
Again, the two are in the preferred order, although the difference between how much I prefer one over the other is significantly less.
Getter Access
Make a private getter method that returns the test double or the default object, which any code that originally used the default object can call to get their object.
Truth be told, I find very little benefit to using a getter over direct field access. The biggest help it provides is when you have the field set to null to signify to use the default, which, in itself, is not usually the best design, since a null check is slower than straight access. There is a time where this might not be the case, which is brought up in the section, Instances and Factories.
Field Access
Just use the private field when you need to use the double or the default. Itâs really simple.
This is okay as access largely because itâs only happening within the class, which is allowed to use its own implementation details.
Overload Wrapping or âHidden Call Injectionâ
This technique ignores the idea of using package-private suppliers and private accessors and works in a very different way, not even needing the addition of a field to the class. Itâs especially useful if only a single method uses the object that needs a double (though, that situation might suggest that your class is breaking the SRP).
The technique involves overloading the method you want to test with a package-private method that accepts the dependency as a parameter. You then move all the logic from the original into the overload, replacing the locked-in dependency with the one passed in by the parameter. Then the original method simply calls the new overload and passes in the original object. For example, you can turn this
public void myFunc()
{
MyType object = new MyType();
object.doSomething();
this.thingy = object.getSomething();
}into this
public void myFunc()
{
MyType object = new MyType();
myFunc(object);
}
void myFunc(MyType object)
{
object.doSomething();
this.thingy = object.getSomething();
}Now you can pass in test doubles and isolate the functionality of myFunc().
Instances and Factories
There is one thing youâll have to watch out for when refactoring like this: how often new instances are needed. If you need a new instance of the depended-on object every time you run a function, then all suppliers need to pass in a factory for creating the object instead of instances of the object. With overload wrapping, you can usually pass in single instances, unless the individual method itself makes multiple copies of the object.
If you havenât read it yet, I wrote a post a little bit ago about making simple functional factories in Java and Python.
Test the Defaults
Donât forget: you still need to test whether the it works with your default instance, too. If it runs slowly, Iâd put it into your integration tests.
Disappointing Side Effect
Your tests are supposed to be living documentation about how your class can be used. Using these techniques, unfortunately, teaches the reader about ways that arenât actually available to them. Maybe, just maybe, you should consider not being so stubborn about what specific class is used inside?
Itâs totally up to you; I was just providing a disclaimer (and a hint :) ).
Awesome Side Benefit?
One really cool side-effect of refactoring like this is the fact that, if you ever change your mind and want to allow your code users to inject their own versions of the dependency, all you need to do is set your suppliers and overloads to public.
In fact, you can take this entire article and replace every instance of âpackage-privateâ with âpublicâ to make it a general article about providing dependency injection.
| Reference: | Refactoring to Allow Test Doubles from our JCG partner Jacob Zimmerman at the Programming Ideas With Jake blog. |


