Mockito: Inject real objects into private @Autowired fields

JavaSpringMockito

Java 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

Mockito-home

@Spy

@Mock

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();
 }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser2286693View Question on Stackoverflow
Solution 1 - JavaDev BlankedView Answer on Stackoverflow
Solution 2 - JavaYoaz MendaView Answer on Stackoverflow
Solution 3 - JavadavidxxxView Answer on Stackoverflow
Solution 4 - JavatakacsotView Answer on Stackoverflow
Solution 5 - JavaJonathan S. FisherView Answer on Stackoverflow