Я тестировал @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, чтобы обеспечить правильность тестов - это очень разные вещи.
Да, есть некоторые преимущества для этого шаблона, но это не то, о чем мой вопрос. Я спрашиваю, как наилучшим образом ограничить объем конфигурации в пакете, отсканированном компонентом. – jwilner
Изменение упаковки будет противоречить локальному охвату пакета, что, очевидно, важно для тестирования. Это неудовлетворительно. – jwilner