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
注释为其他测试提供干净的工作表。是的,该模式有一些优点,但这不是我的问题所在。我在问如何最好地限制组件扫描包内的配置范围。更改包将与包本地范围冲突,这显然对测试很重要。这是不能令人满意的。