Mockito: Inject real objects into private @Autowired fields
JavaSpringMockitoJava Problem Overview
I'm using Mockito's @Mock
and @InjectMocks
annotations to inject dependencies into private fields which are annotated with Spring's @Autowired
:
@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
@Mock
private SomeService service;
@InjectMocks
private Demo demo;
/* ... */
}
and
public class Demo {
@Autowired
private SomeService service;
/* ... */
}
Now I would like to also inject real objects into private @Autowired
fields (without setters). Is this possible or is the mechanism limited to injecting Mocks only?
Java Solutions
Solution 1 - Java
Use @Spy
annotation
@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
@Spy
private SomeService service = new RealServiceImpl();
@InjectMocks
private Demo demo;
/* ... */
}
Mockito will consider all fields having @Mock
or @Spy
annotation as potential candidates to be injected into the instance annotated with @InjectMocks
annotation. In the above case 'RealServiceImpl'
instance will get injected into the 'demo'
For more details refer
Solution 2 - Java
In Addition to @Dev Blanked answer, if you want to use an existing bean that was created by Spring the code can be modified to:
@RunWith(MockitoJUnitRunner.class)
public class DemoTest {
@Inject
private ApplicationContext ctx;
@Spy
private SomeService service;
@InjectMocks
private Demo demo;
@Before
public void setUp(){
service = ctx.getBean(SomeService.class);
}
/* ... */
}
This way you don't need to change your code (add another constructor) just to make the tests work.
Solution 3 - Java
Mockito is not a DI framework and even DI frameworks encourage constructor injections over field injections.
So you just declare a constructor to set dependencies of the class under test :
@Mock
private SomeService serviceMock;
private Demo demo;
/* ... */
@BeforeEach
public void beforeEach(){
demo = new Demo(serviceMock);
}
Using Mockito spy
for the general case is a terrible advise. It makes the test class brittle, not straight and error prone : What is really mocked ? What is really tested ?
@InjectMocks
and @Spy
also hurts the overall design since it encourages bloated classes and mixed responsibilities in the classes.
Please read the spy()
javadoc before using that blindly (emphasis is not mine) :
> Creates a spy of the real object. The spy calls real methods unless
> they are stubbed. Real spies should be used carefully and
> occasionally, for example when dealing with legacy code.
>
> As usual you are going to read the partial mock warning
: Object
> oriented programming tackles complexity by dividing the complexity
> into separate, specific, SRPy objects. How does partial mock fit into
> this paradigm? Well, it just doesn't... Partial mock usually means
> that the complexity has been moved to a different method on the same
> object. In most cases, this is not the way you want to design your
> application.
>
> However, there are rare cases when partial mocks come handy: dealing
> with code you cannot change easily (3rd party interfaces, interim
> refactoring of legacy code etc.) However, I wouldn't use partial mocks
> for new, test-driven & well-designed code.
Solution 4 - Java
In Spring there is a dedicated utility called ReflectionTestUtils
for this purpose. Take the specific instance and inject into the the field.
@Spy
..
@Mock
..
@InjectMock
Foo foo;
@BeforeEach
void _before(){
ReflectionTestUtils.setField(foo,"bar", new BarImpl());// `bar` is private field
}
Solution 5 - Java
I know this is an old question, but we were faced with the same problem when trying to inject Strings. So we invented a JUnit5/Mockito extension that does exactly what you want: https://github.com/exabrial/mockito-object-injection
EDIT:
@InjectionMap
private Map<String, Object> injectionMap = new HashMap<>();
@BeforeEach
public void beforeEach() throws Exception {
injectionMap.put("securityEnabled", Boolean.TRUE);
}
@AfterEach
public void afterEach() throws Exception {
injectionMap.clear();
}