1

I try to do the following test:

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;


@ExtendWith(MockitoExtension.class)
public class SomeClassTest {
    
    // needs to be mocked and injected
    @Mock
    private SomeDao someDao;

    @InjectMocks
    private SomeClass someClass = new someClass();

    @Test
    void test() {
       when(someDao.retrieveSomeData).thenReturn(10);
    }
}

This the simplified class where the object I want to inject is instantiated and used in a method:

public class SomeClass {
   private void someMethod() {
      SomeDao someDao = new SomeDao();
      someDao.retrievSomeData();
   }
}

But I can also write the class as following with the instantiation in the constructor:

public class SomeClass{
   SomeDao someDao;

   public SomeClass() {
      someDao = new SomeDao();
   }

   private void someMethod() {
      someDao.retrievSomeData();
  }
}

Result: It is still the real function with the reals object someDao.rerieveSomeData() called and not my mocked object! Why? I am rather confused.

EricSchaefer
  • 25,272
  • 21
  • 67
  • 103
Wumba
  • 99
  • 1
  • 12
  • You always create a new SomeDao in SomeClass instead of passing it as a parameter in the constructor. Thus your mock dao is not injected. – Lesiak Apr 09 '21 at 17:08

1 Answers1

1

@InjectMock can inject mocks in three ways:

  1. Constructor injection: If your SomeClass has a constructor parameter of type SomeDao it will pass the mock as that parameter.
  2. Setter injection: If SomeClass has a single setter method with a parameter of type SomeDao (e.g. setDao(SomeDao dao) or there are several such setters, but one has the same name as the mock (i.e. setSomeDao(SomeDao someDao)), it will call this setter with the mock as the parameter.
  3. Field injection: If there is a single field of type SomeDao or there are several fields of type SomeDao, but one field has the same name as your mock (i.e. someDao), it will set this field with the mock.

For your code this means you should never create an instance of SomeDao in your SomeClass class, but instead inject it. In your normal business code you would create the DAO outside of SomeClass and pass it to the constructor (which is usually the prefered injection variant). In your test mockito would then take care to do the same, but with the mock instead of the real DAO.

EricSchaefer
  • 25,272
  • 21
  • 67
  • 103
  • But when I pass the object to the constructor, is `@InjectMocks` still necessary? I could create a mock and pass it to the constructor in my test: `SomeClass someClass = new SomeClass(mockedSomeDao)? – Wumba Apr 14 '21 at 08:58
  • Ah I realize, I could either use @InjectMocks for constructor injection or pass the mocked classes myself, there is no difference as seen in this answer: https://stackoverflow.com/a/23087583/11431537 Thank you very much @EricSchaefer! – Wumba Apr 14 '21 at 10:13
  • 1
    You got it. InjectMocks is used if you need to inject multiple things in many test. It saves you the work of doing the injection manually. – EricSchaefer Apr 14 '21 at 18:07