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 аннотированные поля с соответствующими полями, в противном случае Mockito может запутаться и инъекции этого не случится.

означает ли это, что если у меня есть несколько полей с одинаковым типом, я не могу издеваться только над одним из них, а должен определить @Mock на все поля с одинаковым типом? Известно ли ограничение и есть ли причина, по которой оно еще не было исправлено? Это должно быть просто, чтобы соответствовать @Mock имена полей, не так ли?

2 ответов


похоже, Mockito использует алгоритм, описанный в их JavaDoc

Если я правильно понимаю, он сначала сортирует по типу (в данном случае только 1 B), а затем сортирует по имени (здесь нет изменений). Он, наконец, впрыснет с помощью реализация интерфейса OngoingInjector, который, похоже, ищет первое поле и вводит его.

поскольку у вас есть только 1 B, определенный, и есть 2 поля B в макете, он увидит совпадение первый экземпляр на поле и стоп. Это потому что mocks.size() == 1 на NameBasedCandidateFilter . Поэтому он остановит фильтровать и впрыснет его сразу. Если вы создадите несколько насмешек одного типа, они будут отсортированы по имени и введены соответственно.

я смог заставить его работать, когда я создал несколько насмешек (но меньше, чем количество полей) определенного типа.

@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 и.В2 (первые 2 поля, определенные в).

вы всегда можете оставить проблему GitHub в своем репозитории с улучшением или изменением алгоритма фильтрации инъекций, чтобы посмотреть.


это задокументировано в mockito как работа вокруг, если существует несколько насмешек одного и того же типа. Он не разрешает реализацию на основе предоставленного имени (ie @Mock(name = "b2")). Алгоритм, который он использует для разрешения реализации, - это имя поля введенной зависимости. Таким образом, ваш код выше будет правильно разрешен (b2 =>@Mock private B b2 и b3 =>@Mock private B b3).

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