Java 从@ComponentScan中排除测试类中的配置

Java 从@ComponentScan中排除测试类中的配置,java,spring,unit-testing,junit,spring-java-config,Java,Spring,Unit Testing,Junit,Spring Java Config,我在测试的@ComponentScan类中遇到了@Configuration问题,也就是说,@ComponentScan在集成测试期间引入了意外的@Configuration 例如,假设您在src/main/java中有一些全局配置,它在com.example.service,com.example.config.GlobalConfiguration中引入组件: package com.example.config; ... @Configuration @ComponentScan(base

我在测试的
@ComponentScan
类中遇到了
@Configuration
问题,也就是说,
@ComponentScan
在集成测试期间引入了意外的
@Configuration

例如,假设您在
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”)
注释(为简洁起见省略)

然后在src/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...);
         }
    }
}
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...);
         }
    }
}
还有
com.example.ServiceBIntegrationTest
,它需要拉入
GlobalConfiguration.class
,才能成为集成测试,但仍然避免拉入带有
@ActiveProfiles(“test”)的危险实现:

ServiceBIntegrationTest
的明显意图是通过
GlobalConfiguration
引入完整的
src/main/java
应用程序配置,通过
@ActiveProfiles(“test”)
排除危险组件,并用自己的实现替换排除的组件。然而,在测试过程中,
src/main/java
src/test/java
的名称空间被组合在一起,因此
GlobalConfiguration
@ComponentScan
在类路径中找到了比通常更多的内容,即
ServiceA.ServiceATestConfiguration
中定义的
ServiceA
bean。这很容易导致冲突和意外结果

现在,您可以在
GlobalConfiguration
上执行一些操作,比如
@ComponentScan(…,excludeFilters=@ComponentScan.Filter(type=FilterType.REGEX,pattern=“\\.*(T)est\\\.])
,但这有其自身的问题。依赖命名约定是相当脆弱的;尽管如此,即使您退出了
@TestConfiguration
注释并使用了
FilterType.annotation
,您的
src/main/java
也会有效地让您的
src/test/java
意识到您的
src/test/java
,这在我看来是不应该的(请参见下面的注释

目前,我通过使用一个附加的配置文件解决了我的问题。在
ServiceA
上,我添加了一个唯一的概要文件名——这样它的概要文件注释就变成了类似于
@ActiveProfiles(“test,serviceatest”)
。然后,在
ServiceATest.ServiceATestConfiguration
上添加注释
@Profile(“ServiceATest”)
。这有效地限制了
ServiceATestConfiguration
的范围,但开销相对较小,但似乎:

a) 我正在错误地使用
@ComponentScan
,或者

b) 应该有一个更干净的模式来处理这个问题

是哪一个



注意:是的,该应用程序支持测试,因为它使用的是
@Profile(“!test”)
,但我认为,让应用程序稍微有一点测试意识,以防止不适当的资源使用,让它有测试意识以确保测试的正确性,这是非常不同的事情。

我看到您试图在集成测试期间伪造SpringBean。如果您将
@Profile
@ActiveProfiles
注释与
@Primary
注释相结合,您的大多数头痛问题都会消失,您不需要用
@Profile(“!test”)标记生产bean

我写了一封信

对评论的反应:

通过包结构。组件扫描扫描当前包和子包中的所有包。如果您不想扫描bean,只需修改您的包结构,这样bean就不会在您的组件扫描保护伞下

Spring没有将包与
src/test/java
src/main/java
区分开来。试图用
@Profile(“!test”)
排除生产bean是一种设计气味。你应该避免。我建议给我一个机会,从提到的博客接近


请注意,当您使用@Primary注释覆盖bean时,您可能需要使用
@DirtiesContext
注释为其他测试提供干净的工作表。

是的,该模式有一些优点,但这不是我的问题所在。我在问如何最好地限制组件扫描包内的配置范围。更改包将与包本地范围冲突,这显然对测试很重要。这是不能令人满意的。