Отражение в нескольких обернутых объектах

#java #reflection #junit #mockito #transactional

#java #отражение #junit #mockito #транзакционный

Вопрос:

В тесте JUnit я хочу изменить шаблон гибернации в Spring DAO. Этот DAO является

  1. аннотируется с помощью @Transactional, поэтому он переносится во время выполнения и
  2. отслеживается методом Mockitos spy(). Таким образом, DAO будет обернут во второй раз этим шпионом.

Итак, в DAO теперь есть два объекта обертывания: один из @Transactional, один из spy. Из-за того, что неизвестно, какая из этих оболочек создается первой, я не могу установить шаблон гибернации в DAO через отражение.

Как я могу установить шаблон в DAO с двойной оболочкой?

[Править]

Некоторый источник:

 /**
* This class gets wrapped by a proxy object because of @Transactional.
*/
@Transactional 
public class MyDao implements SomeDaoInterface { ... }
  

В тестовом классе:

 public class MyTestClass {
@Autowired 
private MyDao myDao;

@Test
public void myTestMethod() throws Exception {
   final MyDao daoSpy = spy(myDao);   // Dao gets wrapped with second wrapper

   final Field field = MyDao.class.getDeclaredField("template");
   field.setAccessible(true);
   field.set(daoSpy, mySpecialMockedTemplate);  // ERROR: want to inject the template but
                                                // dont know in which wrapper
}
}
  

Комментарии:

1. Привет, не могли бы вы добавить несколько примеров кода?

Ответ №1:

Вызовите метод setter вместо доступа к полю.

Комментарии:

1. Использование установщика не решает проблему: Spring обертывает dao из-за @Autowired. Установщик установит любое заданное свойство в эту оболочку, а не dao.

Ответ №2:

Кажется, что ваш код отражения неверен. Используйте этот оператор вместо :

 field.set(daoSpy, mySpecialMockedTemplate);
  

Однако, глядя на ваш тестовый код, кажется, что вы используете Spring для создания MyDao экземпляра. Кажется странным использовать отражение для установки шаблона, почему бы не настроить его весной?
Или даже использовать фактический сеттер? Или сделайте поле защищенным, чтобы только модульный тест мог получить к нему доступ.

РЕДАКТИРОВАТЬ: Что касается внедрения, вы могли бы создать экземпляр DAO в своем тесте и specialMockedTemplate внедрить его с помощью Mockito. Вы могли бы написать что-то вроде :

 @RunWith(MockitoJUnitRunner.class)
public class MyTestClass {
    @InjectMocks private MyDao dao;

    @Mock SpecialTemplate specialTemplate;

    @Test void dao_should_call_the_template_with_parameter_A_and_B() {
        // given

        // when
        dao.someCall("A", "B");

        // then
        verify(specialTemplate).someCallWith("A", "B");
    }
}
  

Несколько предупреждений, однако, избегайте частичного издевательства, если это возможно (используя шпионов). Избегайте использования типов, которыми вы не владеете, вы должны прочитать эту запись в блоге, почему это плохая идея.

Комментарии:

1. PowerMockito может помочь здесь, для еще одного варианта.

2. Хотя библиотека PowerMock отлично подходит для кода, который сложно протестировать, я бы настоятельно рекомендовал избегать разработки кода, для которого требуется тестирование Powermock. Это путь к взломам, а код / тестирование сложно поддерживать.

3. Верно, я разместил неправильный код отражения. Использование установщика не решает проблему: Spring обертывает dao из-за @Autowired. Установщик установит любое заданное свойство в эту оболочку, а не dao. Настройка в Spring на самом деле кажется единственным способом сделать это. В качестве альтернативы dao может быть создан вместо автоматической настройки.

4. Mockito предлагает легкую утилиту автоматического внедрения, я отредактировал свой ответ в этом отношении.