Over a million developers have joined DZone.

The Danger Of @InjectMocks

A mocking framework is a common way to isolate code for unit testing. Can a mocking framework give you too much power?

· DevOps Zone

The DevOps zone is brought to you in partnership with Sonatype Nexus. The Nexus suite helps scale your DevOps delivery with continuous component intelligence integrated into development tools, including Eclipse, IntelliJ, Jenkins, Bamboo, SonarQube and more. Schedule a demo today

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 of Mockito’s feature that are used.

For example, Mockito’s developers took a really 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 your code definitely does.

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:

Image title

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.

public class Test {

    private DepA depA = new DepA();

    private DepX depX;

    private DepY depY;

    // tests follow

Since depX and depY are mocked by 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 lose 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 field 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.

The DevOps zone is brought to you in partnership with Sonatype Nexus. Use the Nexus Suite to automate your software supply chain and ensure you're using the highest quality open source components at every step of the development lifecycle. Get Nexus today

mockito,unit testing,reflection

Published at DZone with permission of Nicolas Frankel, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}