对于@SpringBootTest,@TestConfiguration类的@Import不起作用,而@ContextConfiguration按预期覆盖。

4
考虑以下集成测试注释:
@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 或一组 beans,每当我使用 @ContextConfiguration 时,真实的 bean 就会退出,使用来自 WorkerTestConfig 的 bean;每当我使用 @Import 时,真实的 bean 仍然会被创建并使测试失败。
WorkerTestConfig 本身尽可能简单:
@TestConfiguration
public class WorkerTestConfig {

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

请问有人能解释一下@SpringBootTest注解的另一个神奇行为吗?如果您能重现相同的行为,请确认一下,这样我就可以去问题追踪器了,因为我在stackoverflow上看到有人在使用@Import和@SpringBootTest,而且在spring boot文档中也没有禁止这样做:https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-excluding-config 完全不知道发生了什么。
版本:2.1.2.RELEASE
更新:
还尝试过删除真实的bean,以查看问题是否仅出现在覆盖方面,但是@Import注解根本无法工作 -> 甚至无法创建bean,@ContextConfiguration具有添加/覆盖行为,导入根本不起作用。 注解的完全限定导入为: import org.springframework.context.annotation.Import;
还尝试将@TestConfiguration更改为@Configuration,只是为了尝试,但仍然没有任何作用。无效。
更新2: < p> 注意:

然而,@Import在标准的Spring测试中可正常工作:

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

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

如果@TestConfiguration类是嵌套类,它们将自动注册。你试过这个吗? - undefined
1
我假设在第46.3.3节中@Import(MyTestsConfiguration.class)中的MyTestsConfiguration是一个被@TestConfiguration注解标记的类。 - undefined
很酷,我会在Spring问题跟踪器上引用Stack Overflow。 - undefined
反向引用:https://github.com/spring-projects/spring-boot/issues/16328 - undefined
我无法复现您描述的Spring Boot 2.1.2(或当前最新版本2.1.3)的行为。在顶级@TestConfiguration类上使用@Import会导致导入类中的bean被定义。请您更新问题,包含一个最小、完整且可验证的示例 - undefined
显示剩余4条评论
1个回答

7
@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]

当你使用没有classes属性的@Import时,会得到以下结果:

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]

第一版在载入SomeTestSecondConfiguration之前会加载WorkerConfig,而第二版会先加载SomeTestSecondConfiguration再加载WorkerConfig
此外,您会注意到第二个版本有一个ImportsContextCustomizer$ImportsConfiguration类,它是触发额外导入的事物。
如果您查看SpringBootTestContextBootstrapper,您可以在getOrFindConfigurationClasses方法中看到定义排序,您的其他测试类将始终列在主配置之后。
简而言之,如果您需要定义顺序,请使用classes属性。如果您想添加其他bean并且不打算覆盖任何内容,则使用@Import
您可能还想查看@MockBean,它提供了一种更强大的方法来用模拟替换bean。

1
@phil.webb。非常感谢您对注释的清晰解释和意图,以及查看加载内容的便捷技巧。唯一令我困惑的是,“ScheduledExecutorService”遵循您解释的规则和顺序。在导入时,“OriginalInterface”始终被TestConfiguration bean覆盖。请您确认第二个方法'checkWhosGreeting'。当我使用条件断点时,可以看到Original在之后加载,但它并没有覆盖测试方法,ScheduledExecutorService bean与标准bean的工作方式一直不一致 :) - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接