Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

The Danger Of @InjectMocks

DZone's Guide to

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
Free Resource

Download “The DevOps Journey - From Waterfall to Continuous Delivery” to learn learn about the importance of integrating automated testing into the DevOps workflow, brought to you in partnership with Sauce Labs.

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.

@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 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.

Discover how to optimize your DevOps workflows with our cloud-based automated testing infrastructure, brought to you in partnership with Sauce Labs

Topics:
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 DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

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

X

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

{{ parent.tldr }}

{{ parent.urlSource.name }}