首页 > 解决方案 > 对于@SpringBootTest @TestConfiguration 类的@Import 什么都不做,而@ContextConfiguration 按预期覆盖

问题描述

考虑以下集成测试注释:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
                properties = "spring.main.allow-bean-definition-overriding=true")
@ContextConfiguration(classes = {WorkerTestConfig.class})
//@Import(value = {WorkerTestConfig.class})
@ActiveProfiles({"dev","test"})
public class NumberServiceITest {

WorkestTestConfig 的作用是在集成启动期间覆盖真正的 bean/bean 集,每当我使用@ContextConfiguration真正的 bean 并使用来自 WorkerTestConfig 的 bean 时,每当我使用@Import真正的 bean 时,仍然会创建并通过测试。

WorkerTestConfig本身是尽可能微不足道的:

@TestConfiguration
public class WorkerTestConfig {

    @Primary
    @Bean
    public ScheduledExecutorService taskExecutor() {
        return DirectExecutorFactory.createSameThreadExecutor();
    }
}

谁能解释@SpringBootTest注释的另一个神奇行为?如果您重现相同的行为,请确认以便我可以去问题跟踪器,因为我已经看到人们在 SO 上使用@Importwith@SpringBootTest并且在 Spring Boot 文档中没有任何禁止它: https ://docs.spring.io/spring-boot/ docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications- exclude-config

完全不知道发生了什么。

版本:2.1.2.RELEASE

更新:

还尝试删除真正的 bean 以查看问题是否只是覆盖,但@Import注释只是死在水中,不起作用 -> 甚至无法创建 bean,@ContextConfiguration 具有附加/覆盖行为,导入在全部。注释的完全限定导入是:import org.springframework.context.annotation.Import;

也试图从改变@TestConfiguration@Configuration只是为了它,根本没有。死的。

更新 2:

虽然@Import适用于标准弹簧测试:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {Some.class,
                                 Some2WhichDependsOnWorkerTestConfig.class})

@Import(WorkerTestConfig.class)
@ActiveProfiles("test")
public class SomeOtherTest {

标签: spring-bootspring-boot-test

解决方案


@Import未定义在测试中使用类时处理它们的顺序。添加测试功能的@Import主要目的是允许轻松注册其他bean,无意将其用于替换bean 定义。

如果您想深入了解杂草并确切了解发生了什么,您可以打开ConfigurationClassParser并在doProcessConfigurationClass. 添加以下条件代码:

System.err.println(configClass);
return false;

现在,如果您调试应用程序,您将在处理配置类时获得附加输出。

当您使用classes不带注释的属性时,@Import您会看到:

ConfigurationClass: beanName 'demoImportBugApplication', com.example.demoimportbug.DemoImportBugApplication
ConfigurationClass: beanName 'original', class path resource [com/example/demoimportbug/first/Original.class]
ConfigurationClass: beanName 'workerConfig', class path resource [com/example/demoimportbug/first/WorkerConfig.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/scheduling/annotation/ProxyAsyncConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/scheduling/annotation/ProxyAsyncConfiguration.class]
ConfigurationClass: beanName 'someTestSecondConfiguration', com.example.demoimportbug.second.SomeTestSecondConfiguration
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/context/PropertyPlaceholderAutoConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/cache/CacheAutoConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/cache/GenericCacheConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/cache/SimpleCacheConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/cache/NoOpCacheConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/context/ConfigurationPropertiesAutoConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/info/ProjectInfoAutoConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfiguration.class]

当您使用@Import没有classes属性的情况下,您将获得:

ConfigurationClass: beanName 'org.springframework.boot.test.context.ImportsContextCustomizer$ImportsConfiguration', org.springframework.boot.test.context.ImportsContextCustomizer$ImportsConfiguration
ConfigurationClass: beanName 'null', class path resource [com/example/demoimportbug/first/SomeFirstUsingSecondConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [com/example/demoimportbug/second/SomeTestSecondConfiguration.class]
ConfigurationClass: beanName 'demoImportBugApplication', com.example.demoimportbug.DemoImportBugApplication
ConfigurationClass: beanName 'original', class path resource [com/example/demoimportbug/first/Original.class]
ConfigurationClass: beanName 'workerConfig', class path resource [com/example/demoimportbug/first/WorkerConfig.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/scheduling/annotation/ProxyAsyncConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/scheduling/annotation/ProxyAsyncConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/context/PropertyPlaceholderAutoConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/cache/CacheAutoConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/cache/GenericCacheConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/cache/SimpleCacheConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/cache/NoOpCacheConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/context/ConfigurationPropertiesAutoConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/info/ProjectInfoAutoConfiguration.class]
ConfigurationClass: beanName 'null', class path resource [org/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfiguration.class]

第一个版本WorkerConfig在之前加载,SomeTestSecondConfiguration而第二个版本SomeTestSecondConfiguration在之前加载WorkerConfig

您还会注意到第二个版本有一个ImportsContextCustomizer$ImportsConfiguration触发额外导入的类。

如果您看一下,SpringBootTestContextBootstrapper您可以在getOrFindConfigurationClasses方法中看到定义了排序,并且您的附加测试类将始终列在主要配置之后。

tl;dr 如果您需要定义的排序,请使用该classes属性。如果你想添加额外的 bean 并且你不想覆盖任何东西,请使用@Import.

您可能还想看看@MockBean哪个提供了一种更强大的方法来用模拟替换 bean。


推荐阅读