我已经遇到@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...);
}
}
}
还有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
' s @ComponentScan
在类路径中找到比通常更多的位置 - 即, ServiceA
中定义的ServiceA.ServiceATestConfiguration
bean。这很容易导致冲突和意想不到的结果。
现在,您可以在GlobalConfiguration
@ComponentScan(..., excludeFilters= @ComponentScan.Filter(type = FilterType.REGEX, pattern = "\\.*(T|t)est\\.*"))
上执行某些操作,但这有问题。依赖命名约定非常脆弱;即使您退出了@TestConfiguration
注释并使用了FilterType.ANNOTATION
,您仍然可以有效地让src/main/java
了解您的src/test/java
,而不应该{39}} ;是的,IMO(见下面的注意)。
目前,我已经通过使用其他个人资料解决了我的问题。在ServiceA
上,我添加了一个唯一的配置文件名称 - 以便其配置文件注释变为@ActiveProfiles("test,serviceatest")
。然后,在ServiceATest.ServiceATestConfiguration
上添加注释@Profile("serviceatest")
。这有效地限制了ServiceATestConfiguration
的范围,开销相对较小,但似乎要么:
a)我错误地使用@ComponentScan
或
b)应该有一个更清晰的模式来处理这个问题
这是什么?
注意:是的,该应用程序具有测试感知能力,因为它使用的是@Profile("!test")
,但我认为该应用程序会略微提供测试感知以防范不恰当的资源使用并使其具有测试意识以确保测试的正确性是非常不同的。
答案 0 :(得分:2)
我发现你正试图在集成测试期间伪造Spring bean。如果您将@Profile
和@ActiveProfiles
注释与@Primary
注释结合使用,那么您的大部分麻烦都应该消失,您不需要使用@Profile("!test")
标记生产bean。
我用blog post on the topic写了Github examples。
评论反应:
按包结构。组件扫描会扫描当前包和子包中的所有包。如果您不想扫描bean,只需修改您的包结构,就像bean不在组件扫描伞下一样。
Spring不会将包与src/test/java
或src/main/java
区分开来。尝试用@Profile("!test")
排除生产bean是设计气味。你应该避免它。我建议有机会接近提到的博客。
请注意,当您使用@Primary注释覆盖bean时,可能需要使用@DirtiesContext
注释来为其他测试提供干净的工作表。