从@ComponentScan

时间:2016-03-30 19:54:10

标签: java spring unit-testing junit spring-java-config

我已经遇到@ComponentScan个问题的@Configuration个问题 - 即@ComponentScan在集成测试期间引入了非预期的@Configuration

例如,假设您在src/main/java中获得了一些全局配置,其中包含com.example.servicecom.example.config.GlobalConfiguration中的组件:

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

它打算提供两个服务com.example.services.ServiceAcom.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/javasrc/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"),但我认为该应用程序会略微提供测试感知以防范不恰当的资源使用并使其具有测试意识以确保测试的正确性是非常不同的。

1 个答案:

答案 0 :(得分:2)

我发现你正试图在集成测试期间伪造Spring bean。如果您将@Profile@ActiveProfiles注释与@Primary注释结合使用,那么您的大部分麻烦都应该消失,您不需要使用@Profile("!test")标记生产bean。

我用blog post on the topic写了Github examples

评论反应:

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

Spring不会将包与src/test/javasrc/main/java区分开来。尝试用@Profile("!test")排除生产bean是设计气味。你应该避免它。我建议有机会接近提到的博客。

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