Как работает Mockito @InjectMocks?

Вот мой вопрос:

У меня есть несколько классов веб-сервисов для проверки того, что все наследуют их методы от общей службы. Вместо того, чтобы писать unit test для каждого, я полагаю, что я могу сломать набор тестов по функциональным областям (т.е. Три группы тестовых методов, каждая из которых полагается на другой метод вызова метода DAO).

Я предлагаю:

@Mock StateDAO mockedStateDao;
@Mock CountyDAO mockedCountyDao;
@Mock VisitorDAO mockedVisitorDao;

затем вызовите:

@InjectMocks CountyServiceImpl<County> countyService = new CountyServiceImpl<County>();
@InjectMocks StateServiceImpl<State> stateService = new StateServiceImpl<State>();
@InjectMocks VisitorServiceImpl<Visitor> visitorService = new VisitorServiceImpl<Visitor>();

Как я могу быть уверенным, что каждый mockedDAO будет введен в правильный сервис? Было бы проще автоувеличивать все три (а не использовать @InjectMocks)?

Я использую Spring, Hibernate и Mockito...

Ответ 1

Ну, Николас отвечает почти правильно, но вместо того, чтобы угадывать, просто взгляните на javadoc InjectMocks, он содержит больше деталей;)

Для меня странно иметь так много услуг в одном тесте, это не кажется правильным, как unit test или как тест интеграции. В unit test это неправильно, потому что у вас слишком много соавторов, оно не похоже на объектно-ориентированное (или SOLID). В тестах интеграции это странно, потому что код, который вы тестируете, интегрируется с БД, не издевается над ним.

Для быстрой ссылки в 1.9.5 вы имеете:

Отметьте поле, в котором должна выполняться инъекция.

Позволяет сокращать макет и шпионскую инъекцию. Минимизирует повторяющуюся ложную и шпионскую инъекцию. Mockito будет пытаться вводить издевательства только путем инъекции конструктора, инъекции установщика или инъекции свойств по порядку и, как описано ниже. Если какая-либо из следующих стратегий не удалась, то Mockito не сообщит об отказе; то есть вам придется самостоятельно предоставлять зависимости.

  • Встраивание конструктора выбирается самый большой конструктор, тогда аргументы разрешаются с помощью mocks, объявленных только в тесте.

    Примечание. Если аргументы не найдены, то передается значение null. Если требуются немыслимые типы, то инъекция конструктора не будет выполняться. В этих случаях вам придется самостоятельно выполнять зависимости.

  • Внесение setterter свойств сначала будут разрешены по типу, тогда, если есть несколько свойств одного и того же типа, по совпадению имени свойства и имени mock.

    Примечание 1: Если у вас есть свойства с тем же типом (или с тем же стиранием), лучше назвать все аннотированные поля @Mock соответствующими свойствами, иначе Mockito может запутаться, и инъекции не произойдет.

    Примечание 2: Если экземпляр @InjectMocks ранее не инициализирован и не имеет конструктора no-arg, он будет инициализирован этим конструктором.

  • Вставка поля; mocks сначала будет разрешаться по типу, а затем, если имеется несколько свойств одного и того же типа, совпадением имени поля и имени фальшивки.

    Примечание 1: Если у вас есть поля с тем же типом (или с тем же стиранием), лучше назвать все аннотированные поля @Mock соответствующими полями, иначе Mockito может запутаться, и инъекции не произойдет.

    Примечание 2: Если экземпляр @InjectMocks ранее не инициализирован и не имеет конструктора no-arg, он будет инициализирован этим конструктором.

Ответ 2

Если у вас несколько служб и вы хотели бы заменить DAO на Mock-Objects в среде с Spring, я бы рекомендовал использовать Springockito: https://bitbucket.org/kubek2k/springockito/wiki/Home

который также упоминается здесь: Инъекция Mockito превращается в Spring bean

Затем ваш Testclass может выглядеть следующим образом:

@RunWith (SpringJUnit4ClassRunner.class)
@ContextConfiguration (loader = SpringockitoContextLoader.class, locations =    {"classpath:/org/example/package/applicationContext.xml"})
public class NameOfClassTest {

    @Autowired
    @ReplaceWithMock 
    StateDAO mockedStateDao;

    @Autowired
    @ReplaceWithMock 
    CountyDAO mockedCountyDao;

    @Autowired
    @ReplaceWithMock 
    VisitorDAO mockedVisitorDao;

В своем @Test или @Before Methode вы можете настроить свои макеты стандартным способом Mockito:

Mockito.doReturn(null).when(mockedCountyDao).selectFromDB();

Ответ 3

Ну, статический метод MockitoAnnotations.initMocks(Object) используется для загрузки всего процесса.

Я не знаю точно, как это работает, поскольку я не просматривал исходный код, но я бы выполнил его примерно так:

  • Сканировать пройденный класс Object для переменных-членов с аннотацией @Mock.
  • Для каждого из них создайте макет этого класса и установите его этому члену.
  • Сканировать пройденный класс Object для переменных-членов с аннотацией @InjectMocks.
  • Сканировать класс каждого найденного элемента для его членов, которые могут быть введены одним из макетных объектов, созданных в (2) (то есть, где поле является родительским классом/интерфейсом или тем же классом, что и mock objects объявил класс) и установить его для этого элемента.

Ответ 4

Nevermind, посмотрев онлайн, аннотация InjectMocks рассматривает что-либо с аннотацией @Mock как полем и имеет статическую область (класс), поэтому я действительно не мог гарантировать, что маки будут обращаться к правильному сервису. это был несколько мысленный эксперимент по попытке unit test на уровне функции, а не на уровне класса. Угадайте, что я просто буду autowire этот материал с Spring...