Java's ServiceLoader и тестовые ресурсы

У меня есть веб-приложение, которое определяет интегратор Hibernate как часть спецификации Java ServiceLoader, например:

src / main / resources / META-INF / services / org.hibernate.integrator.spi.Integrator

  # Define integrators that should be instantiated by the ServiceLoader
  org.emmerich.MyIntegrator

Это делается в соответствии с руководством по Hibernate здесь.

Моя проблема в том, что когда я пытаюсь выполнить модульные тесты, основной дескриптор интегратора все еще анализируется и создается. Это означает, что, поскольку я имитирую большую часть приложения в модульных тестах, когда интегратор пытается запустить его, он обнаруживает ошибки, которые приводят к сбою моих тестов.

Я определил тот же файл в тестовых ресурсах:

src / test / resources / META-INF / services / org.hibernate.integrator.spi.Integrator

  # Empty file to try and overwrite the main deployment description.

но вместо этого я обнаружил, что анализируются как тестовые, так и основные файлы интегратора.

Я ожидал, что тестовый ресурс перезапишет основной ресурс, тем самым отрисовав основной ресурс obscolete, но это не то, что происходит. Поскольку оба файла находятся в пути к классам (я запускаю тесты через Maven с плагином surefire, который помещает как test-classes, так и classes в путь к классам). То же самое и с persistence.xml.

В моей среде модульного тестирования я не хочу, чтобы какие-либо интеграторы создавались, потому что я хочу управлять построением этих bean-компонентов как можно более вручную. Учитывая, что я тестирую единицы выполнения, мне не нужны дополнительные bean-компоненты, такие как интеграторы, которые могут повлиять на выполнение тестов. Я считаю, что это вполне законное требование во время модульного тестирования. Однако, хотя основные ресурсы все еще анализируются ServiceLoader, это невозможно.

Решение, к которому я пришел, основано на решении persistence.xml, опубликованном здесь:

Как настроить JPA для тестирования в Maven

Мой вопрос: есть ли лучший способ исключить основные ресурсы из обработки во время модульного тестирования, чем принудительное переименование, особенно в контексте ServiceLoader файлов?

Чтобы попытаться резюмировать это немного лучше:

Что произойдет, если у вас есть два файла, названные в честь одного и того же интерфейса службы в пути к классам? Мне кажется, что все службы в обоих файлах созданы. Перезаписи вроде бы нет.


person EMMERICH    schedule 19.04.2013    source источник


Ответы (1)


Для тех, кто заинтересован, небольшое дополнительное исследование привело к тому, что это была не проблема Hibernate, а скорее то, как ServiceLoader загружается в файлы. Из API:

Если конкретный конкретный класс поставщика назван более чем в одном файле конфигурации или назван в одном файле конфигурации более одного раза, то дубликаты игнорируются.

К сожалению, это относится только к конкретным классам. Итак, если я укажу две копии одного и того же файла в форме:

org.emmerich.MyServiceInterface

  org.emmerich.MyServiceImpl

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

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

В любом случае решение, которое я придумал, заключалось в том, чтобы делегировать переопределение основных ресурсов файлу, над которым я мог контролировать, например, файлу properties. Теперь внутри службы я проверяю, является ли флаг в этом файле свойств истинным или ложным. В моем основном ресурсе это правда. В моем тесте это ложь. Поскольку поиск свойств попадает в первый файл в пути к классам, я знаю, что он получит тестовый ресурс. Моя структура выглядит так:

src
  main
    resources
      META-INF
        services
          org.hibernate.integrator.spi.Integrator # has line MyIntegrator
    application.properties     # shouldIntegrate=true 
  test
    resources
      application.properties   # shouldIntegrate=false

И в MyIntegrator:

public class MyIntegrator implements Integrator {

  @Override
  public void integrate(Configuration configuration, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) {
    if(shouldIntegrate()) {
      // do integration
    }
  }

  private boolean shouldIntegrate() {
    // read Properties file from classpath
    // getClass().getClassLoader().getResourceAsStream("application.properties")
    // return value of "shouldIntegrate"
  }

Когда я запускаю в тестовой среде, поиск файла свойств указывает на тестовый ресурс. Вне тестовой среды он указывает на основной ресурс.

person EMMERICH    schedule 19.04.2013