Last week, I wrote about the ways to initialize your Mockito’s mocks and my personal preferences. I’m still working on my legacy project, and I wanted to go deeper into some Mockito’s feature that are used.
For example, Mockito’s developers took a real strong opinionated stance on the design: Mockito can only mock public non-final instance methods. That’s something I completely endorse. To go outside this scope, you’d have to use PowerMock (which I wrote about a while ago). That’s good, because for me, spotting PowerMock on the classpath is a sure sign of a code smell. Either you’re using a library that needs some design improvement… or you code definitely should.
However, I think Mockito slipped some dangerous abilities in its API, akin to PowerMock. One such feature is the ability to inject your dependencies’s dependencies through reflection. That’s not clear? Let’s have an example with the following class hierarchy:
Rules of unit testing would mandate that when testing ToTest
, we would mock dependencies DepA
and DepB
.
Let’s stretch our example further, that DepA
and DepB
are classes that are:
- Out of our reach, because they come from a third-party library/framework
- Designed in a way that they are difficult to mock i.e. they require a lot of mocking behavior
In this case, we would not unit test our class only but integration test the behavior of ToTest
, DepA
and DepB
.
This is not the Grail but acceptable because of the limitations described above.
Now let’s imagine one more thing: DepA
and DepB
are themselves dependent on other classes.
And since they are badly designed, they rely on field injection through @Autowiring
- no constructor or even setter injection is available.
In this case, one would have to use reflection to set those dependencies, either through the Java API or some utility class like Spring’s ReflectionTestUtils
.
In both cases, this is extremely fragile as it’s based on the name of the attribute:
DepA depA = new DepA();
DepX depX = new DepX();
DepY depY = new DepY();
ReflectionTestUtils.setField(depA, "depX", depX);
ReflectionTestUtils.setField(depA, "depY", depY);
Mockito offers an easy alternative to this method: by using @InjectMocks
, Mockito is able to automatically inject mocked dependencies that are in context.
@RunWith(MockitoJUnitRunner.class)
public class Test {
@InjectMocks private DepA depA = new DepA();
@Mock private DepX depX;
@Mock private DepY depY;
// tests follow
}
Since depX
and depY
are mocked my Mockito, they are in context and thus can automatically be injected in depA
by Mockito.
And because they are mocks, they can be stubbed for behavior.
There are a couple of drawbacks though.
The most important one is that you loose explicit injection - also the reason why I don’t use autowiring.
In this case, your IDE might report depX
and depY
as unused.
Or even worse, changes in the initial structure of DepA
won’t trigger any warning for unused fields.
Finally, as for reflection, those changes may result in runtime exceptions.
The most important problem of @InjectMocks
, however, is that it’s very easy to use, too easy…
@InjectMocks
hides the problems of both fields injection and too many dependencies.
Those should hurt but they don’t anymore when using @InjectMocks
.
So if applied to dependencies from libraries - like depA
and depB
, there’s no choice; but if you start using it for your own class aka ToTest
, this for sure seems like a code smell.