2016-12-10 6 views
1

Я просто озадачен этим (Mockito 1.10):Mockito - какие правила управляют инъекциями насмешек подобных классов коллекций?

@Rule 
public MockitoRule rule = MockitoJUnit.rule(); 

@Mock 
private Collection<IndexableField> mockedFieldsFromRetrievedDocument; 

@Spy 
@InjectMocks 
private IndexManager injectedSpyIM = new IndexManager(); 

@Test 
public void numberOfLDocsShouldBePrintedOutWithEachHitLine() throws Exception{ 

    LOGGER.info(String.format("# A: %d", mockedFieldsFromRetrievedDocument.hashCode())); 
    LOGGER.info(String.format("# fFRD %s", injectedSpyIM.getFFRD())); 

Естественно, существует метод getFFRD в IndexManager, который возвращает частное поле,

private Collection<IndexableField> fieldsFromRetrievedDocument; 

Существует также другое частное поле IndexManager:

private Collection<Closeable> closeableComponents; 

Первая зафиксированная строка дает правильный хэш-код.
Последняя строка говорит

# fFRD Null

Когда я потом пошел и исследовал значение closeableComponents я обнаружил, что его хэш-код был именно нагнетаемой макете Collection.

Затем я попытался обменивать позиции деклараций этих полей в IndexManager: без изменений.

Оказывается, что @Mock линия здесь 1) полностью игнорируя общий класс и 2) запирается на к Collection<Closeable> в предпочтении к другим причинам я не понимаю ...

НЕМНОГО позже

Вау, сумасшедшие вещи: я просто изменил название поля closeableComponents к xcloseableComponents. Теперь издеваемое поле действительно делает то, что я хочу, т. Е. Издеваясь над полем fieldsFromRetrievedDocument.

Мой условный вывод, естественно, заключается в том, что Mockito использует первое имя поля типа Collection<anything>, которое оно находит ... в алфавитном порядке! Предположительно, один и тот же процесс отбора применяется к другим случаям, когда имеется более одного поля типа «тот же». Просто безуспешно смотрел на него: кто-нибудь знает, где это где-то документировано?

еще позже

Следуя совету Джефф Боуман я изменил вещи, как так:

@Mock(name="fieldsFromRetrievedDocument") 
private Collection<?> mockedFieldsFromRetrievedDocument; 

... это точное написание, при правильном случае поля в классе , Но он все еще вводил неправильный Collection<?> в качестве издевки. Затем ...

Я изменил с Mockito с 1 по 10, 2.3.0: проблема решена! Предостерегающий рассказ о том, что атрибут name полностью задокументирован в Javadoc API 1.10 ...!

ответ

2

@InjectMocks documentation описывает поведение, которое может быть менее документированное или детерминированным, чем вы бы предпочли:

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

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

Это имеет некоторый смысл, потому что общий тип поля стирается - не читается во время выполнения - и потому, что методы отражения в Java getDeclaredFields и getDeclaredMethods возвращается "not in any particular order". Соответствующие имена предпочтительны, а все остальное - неопределенное поведение, которое ваше переименование происходит, чтобы манипулировать в ваших интересах; не рассчитывайте на это поведение.

Концепция именования насмехающийся выше, относится к использованию атрибута на @Mock annotationname.

+0

Еще раз спасибо. В следующий раз я попробовал дать тестовому классу инъецированный макет с тем же именем, что и фактическое поле в CUT. Я не думал, что это сработает ... и я был прав! Но теперь я знаю, что аннотация '@ Mock' имеет необязательный атрибут« name ». У меня есть над чем работать. Возможно, вы могли подумать, что, столкнувшись с двумя полями того же типа, их фактические имена можно сравнить с их действительными именами, поскольку отражение используется, если '' mockedFoo '.containsIgnoringCase ("foo") ' то предполагается, что 'mockedFoo' стоит за' foo', при отсутствии каких-либо двусмысленностей. Кажется, нет! –

+0

PS мысль № 2: получение дженериков путем отражения: некоторые мысли здесь: http://stackoverflow.com/questions/1901164/get-type-of-a-generic-parameter-in-java-with-reflection. Не могли ли Mockito попробовать sthg в этом направлении? –

+0

@mike Пожалуйста, ознакомьтесь с тем, как «это невозможно сделать» - это правильный ответ на этот вопрос. Самое близкое, что вы можете получить (это то, на что ссылаются большинство ответов), - это то, что для 'класса B extends A ' или 'new A () {}' вы можете отразить A и T из иерархии _type_. Существует способ получить информацию о родовом типе [из определения поля] (http://stackoverflow.com/q/1868333/1426891), но с учетом переменных типа типа вы просите Mockito получить очень умный за счет читаемости и четкости рамок. –