2016-03-30 4 views
5

Я тестировал @ComponentScan вопросов с классами @Configuration, а именно: @ComponentScan во время интеграционных тестов вытаскивает непреднамеренно @Configuration.Исключая конфигурацию в тестовых классах из @ComponentScan

Например, скажем, у вас есть некоторые глобальные конфигурации в src/main/java, который втягивает в компонентах в com.example.service, com.example.config.GlobalConfiguration:

package com.example.config; 
... 
@Configuration 
@ComponentScan(basePackageClasses = ServiceA.class) 
public class GlobalConfiguration { 
    ... 
} 

Он предназначен, чтобы тянуть в двух служб, com.example.services.ServiceA и com.example.services.ServiceB, аннотированный с @Component и @Profile("!test") (опущен для краткости).

Затем в И/тест/Java, com.example.services.ServiceATest:

package com.example.services; 
... 
@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(classes = ServiceATest.ServiceATestConfiguration.class) 
public class ServiceATest { 
    ... 
    @Configuration 
    public static class ServiceATestConfiguration { 
     @Bean 
     public ServiceA serviceA() { 
      return ServiceA(somemocking...); 
     } 
    } 
} 

А также com.example.ServiceBIntegrationTest, который должен тянуть в GlobalConfiguration.class, чтобы быть тестом интеграции, но до сих пор избегает втягивая опасные реализации с @ActiveProfiles("test"):

package com.example.services; 
... 
@RunWith(SpringJUnit4ClassRunner.class) 
@ActiveProfiles("test") 
@ContextConfiguration(classes = {GlobalConfiguration.class, ServiceBIntegrationTest.ServiceBIntegrationTestConfiguration.class}) 
public class ServiceBIntegrationTest { 
    ... 
    @Configuration 
    public static class ServiceBIntegrationTestConfiguration { 
     @Bean 
     public ServiceB serviceB() { 
      return ServiceB(somemocking...); 
     } 
    } 
} 

очевидное намерение ServiceBIntegrationTest является тянуть в полной конфигурации src/main/java приложений с помощью GlobalConfiguration, исключить опасный компоненты через @ActiveProfiles("test") и замените те исключенные компоненты своими собственными реализациями. Однако во время тестов пространство имен src/main/java и src/test/java объединены, поэтому GlobalConfiguration@ComponentScan находит больше в пути к классам, чем обычно, а именно: ServiceA bean, определенный в ServiceA.ServiceATestConfiguration. Это может легко привести к конфликтам и непредвиденным результатам.

Теперь вы можете сделать что-то на GlobalConfiguration, как @ComponentScan(..., excludeFilters= @ComponentScan.Filter(type = FilterType.REGEX, pattern = "\\.*(T|t)est\\.*")), но у этого есть свои проблемы. Опираясь на соглашения об именах, довольно хрупкий; все же, даже если вы внесли аннотацию @TestConfiguration и использовали FilterType.ANNOTATION, вы фактически делаете свой src/main/java осведомленным о своем src/test/java, которого это не должно быть, IMO (см. примечание ниже).

Как бы то ни было, я решил проблему, используя дополнительный профиль. В поле ServiceA я добавляю уникальное имя профиля, так что его аннотация профиля становится чем-то вроде @ActiveProfiles("test,serviceatest"). Затем, на ServiceATest.ServiceATestConfiguration Я добавляю аннотацию @Profile("serviceatest"). Это эффективно ограничивает сферу ServiceATestConfiguration с относительно небольшими затратами, но кажется, что либо:

а) Я использую @ComponentScan неправильно, или

б) Там должно быть намного чище, шаблон для решения этой проблемы

Что это?


примечание: да, приложение тест-знать, потому что он использует @Profile("!test"), но я бы утверждать, что делает приложение немного тест-Aware для защиты от неправильного использования ресурсов и сделать его тест-Aware, чтобы обеспечить правильность тестов - это очень разные вещи.

ответ

1

Я вижу, что вы пытаетесь подделать весенние бобы во время теста интеграции.Если вы объедините аннотацию @Profile и @ActiveProfiles с аннотацией @Primary, большинство ваших головных болей должно исчезнуть, и вам не нужно будет отмечать производственные бобы @Profile("!test").

Я написал a blog post on the topic с Github examples.

Реакция на комментарий:

По структуре упаковки. Сканирование компонентов проверяет все пакеты в текущем пакете и подпакетах. ЕСЛИ вы не хотите сканировать бобы, просто внесите изменения в структуру вашего пакета так, чтобы bean не находился под зонтиком вашего компонента сканирования.

Весна не дифференцирует пакеты от src/test/java или src/main/java. Попытка исключить производственные бобы с @Profile("!test") - это дизайнерский запах. Вы должны избегать этого. Я бы посоветовал дать возможность подойти из упомянутого блога.

Обратите внимание, что, когда вы переопределяете bean-компонент с @Primary аннотацией, вам может потребоваться использовать аннотацию @DirtiesContext, чтобы иметь чистый лист для других тестов.

+1

Да, есть некоторые преимущества для этого шаблона, но это не то, о чем мой вопрос. Я спрашиваю, как наилучшим образом ограничить объем конфигурации в пакете, отсканированном компонентом. – jwilner

+1

Изменение упаковки будет противоречить локальному охвату пакета, что, очевидно, важно для тестирования. Это неудовлетворительно. – jwilner

Смежные вопросы