To autowire or not to autowire (Take 2)
I received an interesting comment on my last article about some capabilities of Mockito that I didn’t know about:
Damien, I don’t agree with you. You can use @Inject on private fields and still be able to unit test easily with Mockito.
For example let’s take a MyClass class with @Injected private fields. In MyClassTest class, I annotate the MyClass field with @InjectMocks, and dependencies with @Mock.
No need to use ugly setters or whatever, it works : mocked dependencies are injected in MyClass.
So when it comes to testing code, I think @Autowired/@Inject on private fields are still suitable. What do you think ?
As a reminder, here is the initial code that I considered a bad practice because of the autowiring on private fields.
@Named
public class AwesomenessFinder {
@Inject
private BlogAnalyzer blogAnalyzer;
@Inject
private BookmarkService bookmarkService;
public void checkBlog(String url) {
if (!bookmarkService.contains(url) && blogAnalyzer.isInteresting(url)) {
bookmarkService.bookmark(url);
}
}
}
It turns out some mocking libraries are totally capable to deal with these dependencies and inject mocks into the private fields. Here is an example of a test class which works very well thanks to Mockito annotations @InjectMocks and @Mock.
@RunWith(MockitoJUnitRunner.class)
public class AwesomenessFinderTest {
@InjectMocks
private AwesomenessFinder awesomenessFinder;
@Mock
private BookmarkService bookmarkService;
@Mock
private BlogAnalyzer blogAnalyzer;
@Test
public void checkInterestingBlog_bookmarked() {
when(bookmarkService.contains(anyString())).thenReturn(true);
when(blogAnalyzer.isInteresting(anyString())).thenReturn(true);
String url = "whatever";
awesomenessFinder.checkBlog(url);
verify(blogAnalyzer).isInteresting(url);
verify(bookmarkService).bookmark(url);
}
}
So autowiring private fields may not be an anti-pattern after all.
I found 3 potential objections to this statement.
- With these annotations, we can’t have a different mock for each test method, so you may need to reset your mocks between each test. Apparently, the reset() functionality on Mockito was added for this special case of container-injected mocks. That doesn’t look a big deal, so I would say this objection is a bit weak.
@Before public void setUp() { reset(bookmarkService, blogAnalyzer); } - Not all mocking libraries support injection on private fields and we shouldn’t deliberately restrict our choice of mocking library. For example, JMock and EasyMock don’t support this feature, unlike Mockito and JMockit (as a side note, here is a nice comparison matrix for mocking libraries, possibly biased toward JMockit where it is hosted).
At first glance, I thought this argument was strong. However it appears to me now that the testing should adapt to the code and not the other way around. For example, I tend to think that a final or static method should be mockable. I know this is debatable and some TDD fanatics would possibly disagree with me, but after all, I think the more power you can get from your mocking library, the better.
- My last objection may be the only one standing. The auto injection of mocks solved the problem for testing but what if it’s the application which needs to inject different kinds of dependencies into a class? Here you have no choice but to remove the autowiring from your private fields. I had to deal with this scenario very recently at work, so this can certainly happen, however I find this use case relatively rare. So, when it happens, refactoring a class or two is probably not a big deal.
In conclusion, I have to say I changed my mind and I don’t see any issue with autowiring private fields, except if you’re stuck with a limited mocking library. It may be just a matter of personal preference but thinking about it was a good learning exercice.


