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

jwi*_*ner 8 java junit spring unit-testing spring-java-config

我一直在遇到测试类的@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 {
    ...
}
Run Code Online (Sandbox Code Playgroud)

它旨在提供两个服务,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...);
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

而且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...);
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

明显的意图ServiceBIntegrationTest是通过引入完整的src/main/java应用程序配置GlobalConfiguration,排除危险组件@ActiveProfiles("test")并用自己的实现替换那些被排除的组件.然而,在测试的命名空间src/main/javasrc/test/java组合,这样GlobalConfiguration@ComponentScan发现多在classpath中比正常情况-即ServiceA在规定豆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开销相对较小的范围,但似乎要么:

a)我使用@ComponentScan不正确,或

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

这是什么?


注意:是的,该应用程序是测试感知的,因为它正在使用@Profile("!test"),但我认为使应用程序具有轻微的测试意识,以防止不正确的资源使用并使其具有测试感知,以确保测试的正确性是非常不同的事情.

lub*_*nac 3

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

我用Github 示例写了一篇关于该主题的博客文章

对评论的反应:

按封装结构。组件扫描扫描当前包和子包内的所有包。如果您不想扫描 bean,只需修改您的包结构,使 bean 不在您的组件扫描范围内。

Spring 不区分包和src/test/javasrc/main/java。试图排除带有@Profile("!test")设计气味的生产豆。你应该避免它。我建议给一个从提到的博客进行接触的机会。

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

  • 更改包装将与包本地范围发生冲突,这对于测试显然很重要。这是不令人满意的。 (2认同)