Mockito @InjectMocks не работает для полей того же типа
Я был очень удивлен, узнав, что следующий простой пример кода не работает для всех версий Mockito > 1.8.5
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {
@Mock(name = "b2")
private B b2;
@InjectMocks
private A a;
@Test
public void testInjection() throws Exception {
assertNotNull(a.b2); //fails
assertNull(a.b1); //also fails, because unexpectedly b2 mock gets injected here
}
static class A{
private B b1;
private B b2;
}
interface B{}
}
В javadocs (http://docs.mockito.googlecode.com/hg/latest/org/mockito/InjectMocks.html) есть цитата:
Примечание 1: Если у вас есть поля с тем же типом (или с тем же стиранием), это лучше назвать все аннотированные поля @Mock соответствующими полями, иначе Мокито может запутаться, и инъекции не произойдет.
Означает ли это, что если у меня есть несколько полей с одним и тем же типом, я не могу имитировать ТОЛЬКО ОДИН из них, а должен определять @Mock
для полей ВСЕ с тем же типом?
Известно ли это ограничение и есть ли причина, почему он еще не исправлен?
Это должно быть просто, чтобы соответствовать @Mock
по именам полей, не так ли?
Ответы
Ответ 1
Похоже, Mockito использует алгоритм, описанный в их JavaDoc
Если я правильно понял, он сначала сортирует по типу (в данном случае только 1 B), а затем сортирует по имени (здесь нет изменений). Он, наконец, будет использовать реализацию интерфейса OngoingInjector, который, как представляется, ищет первое поле и вводит его.
Поскольку у вас только 1 B определено и есть 2 поля B в Mock, он увидит совпадение первого экземпляра с полем и остановится. Это связано с тем, что mocks.size() == 1
в NameBasedCandidateFilter
, Поэтому он прекращает фильтрацию и вводит ее напрямую. Если вы создадите несколько макетов одного типа, они будут отсортированы по имени и соответственно введены.
Мне удалось заставить его работать, когда я создал несколько mocks (но меньше числа полей) определенного типа.
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {
@Mock(name = "b2")
private B b2;
@Mock(name = "b3")
private B b3;
@InjectMocks
private A a;
@Test
public void testInjection() {
System.out.println(this.a);
}
static class A {
private B b1;
private B b2;
private B b3;
}
interface B {
}
}
Это правильно введет b2 в a.b2 и b3 в a.b3 вместо a.b1 и a.b2 (первые 2 поля, которые определены в A).
Вы всегда можете оставить проблему GitHub в своем репозитории с улучшением или изменением алгоритма фильтрации инъекций для просмотра.
Ответ 2
Это документировано в mockito как работа, если несколько макетов существуют одного типа. Он не разрешает реализацию на основе предоставленного имени (т.е. @Mock(name = "b2")
). Алгоритм, который он использует для разрешения реализации, - это имя поля вложенной зависимости. Таким образом, ваш код выше правильно разрешит (b2
= > @Mock private B b2
и b3
= > @Mock private B b3
).
Другим обходным путем является использование инъекции конструктора, которая является рекомендуемым способом инъекции зависимостей.