272

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?

user2286693
  • 3,007
  • 2
  • 17
  • 17
  • 6
    Normally when you're mocking things, it implies that you don't much care about the concrete object; that you only really care about the behavior of the mocked object. Perhaps you want to do an integration test instead? Or, could you provide a rationale as to why you want mocked and concrete objects living together? – Makoto Nov 28 '13 at 16:02
  • 2
    Well, I'm dealing with legacy code and it would take a lot of when(...).thenReturn(...) statements to setup the mock just to prevent some NPE's and the like. On the other hand, a real object could be used safely for this. So it would be very handy to have an option to inject real objects along with mocks. Even if this may be a code smell, I consider it reasonable in this particular case. – user2286693 Nov 28 '13 at 16:45
  • Don't forget the `MockitoAnnotations.initMocks(this);` in the `@Before` method. I know it's not directly related to the original question, but to anyone coming along later, that would need to be added to make this runnable. – Cuga Dec 07 '15 at 16:06
  • 9
    @Cuga: if you use the Mockito runner for JUnit, (`@RunWith(MockitoJUnitRunner.class)`), you don't need the line `MockitoAnnotations.initMocks(this);` – Clint Eastwood Aug 02 '16 at 17:25
  • 1
    Thanks-- I never knew that and have always been specifying both – Cuga Aug 03 '16 at 18:10
  • @ClintEastwood exists any class to similar work of 'MockitoJUnitRunner' to TestNg? – kran1um Oct 01 '18 at 17:21
  • Also to add to the @Cuga comment, use `@BeforeEach` in case of `JUnit5`. Might help somebody someday. – garg10may Apr 12 '20 at 13:31

5 Answers5

447

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

dkb
  • 4,389
  • 4
  • 36
  • 54
Dev Blanked
  • 8,555
  • 3
  • 26
  • 32
  • 15
    +1: Worked for me... except for String objects. Mockito complains: `Mockito cannot mock/spy following: - final classes - anonymous classes - primitive types` – Adrian Pronk Aug 27 '14 at 00:32
  • Thanks It worked for me too :) To make proxy use mock else for real implementation use spy – Swarit Agarwal Oct 29 '15 at 05:58
  • 2
    @AdrianPronk String is a final class – Christopher Schneider Feb 18 '16 at 20:37
  • use power mock for this – Evgeniy Strepetov Apr 13 '16 at 09:11
  • 4
    On my case, Mockito is **not** injecting a Spy. It does inject a Mock though. The field is private and without a setter. – Victor Basso May 03 '16 at 08:01
  • 10
    BTW, there is no need to `new RealServiceImpl()`, `@Spy private SomeService service;` creates a real object using default constructor before spying on it anyway. – parxier Sep 27 '17 at 01:27
  • So you actually do: 1) create the real instance yourself(`@Spy` only simplifies this into auto-creation with no-args constructor; if the dependency does not possess a no-args constructor, or is limited to use `@RequiredArgsConstructor` because it has some `final` fields, `@Spy` does not work; 2) inject this object into the service if this service has a setter, or a constructor with this field. OK. Not so perfect. – WesternGun Mar 26 '19 at 13:46
  • who can i have this if someService requires dependecly injection? – Akhil Surapuram Apr 12 '20 at 09:39
  • Don't forget `MockitoAnnotations.initMocks(this)` in `before` – dudeNumber4 May 10 '23 at 22:48
21

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.

Yoaz Menda
  • 1,555
  • 2
  • 21
  • 43
12

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
}
takacsot
  • 1,727
  • 2
  • 19
  • 30
  • I was trying to inject Spring's own MockHttpServletRequest. Doing it with @Spy resulted in a my test subject getting a different mock than the one I'd prepared in my test method. Using ReflectionTestUtils has the desired result. – Dominic Cronin Oct 09 '20 at 14:32
10

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.

davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • While some of your points might be correct you didn't answer the question. How would your code look like if you want to use the real implementation of "SomeService" (what is the actual question here) - changing "@Mock" to "@Spy" would help. And speaking about constructor injection - yes! But then use it and create a constructor for you test class ;-). And the text is warning about using "partial mocking" but this question is about using spy with no mocking at all. – dermoritz Oct 06 '20 at 12:06
  • 1
    @dermoritz Changing "@Mock" to "@Spy" doesn't mean using the real implementation. It will use a spy and a spy is a partial mock. And that may make several things which stubing, so it is not the real implementation. You answered to the question : using a constructor to set the field and instantiate the real implementation (with the new idiom() or a bean autowiring with a spring runner, see `@MockBean`) – davidxxx Oct 07 '20 at 07:33
1

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();
 }
Jonathan S. Fisher
  • 8,189
  • 6
  • 46
  • 84