对于@SpringBootTest @TestConfiguration class 的@Import 什么都不做,而@ContextConfiguration 按预期覆盖
For @SpringBootTest @Import of @TestConfiguration class does nothing, while @ContextConfiguration overrides as expected
考虑以下集成测试注释:
@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/set 个 bean,无论何时我使用 @ContextConfiguration
真实的 bean 正在退出并使用来自 WorkerTestConfig 的 bean,无论何时我使用@Import
真正的 bean 仍然被创建并且没有通过测试。
WorkerTestConfig
本身尽可能的琐碎:
@TestConfiguration
public class WorkerTestConfig {
@Primary
@Bean
public ScheduledExecutorService taskExecutor() {
return DirectExecutorFactory.createSameThreadExecutor();
}
}
谁能解释一下 @SpringBootTest 注解的另一个神奇行为?如果您重现相同的行为,请确认,以便我可以转到问题跟踪器,因为我看到有人在 SO 上使用 @Import
和 @SpringBootTest
,并且在 spring 引导文档中没有禁止它:
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有 additive/overriding 行为, import 什么都不做。
注释的完全限定导入是:
导入 org.springframework.context.annotation.Import;
也试图从 @TestConfiguration
更改为 @Configuration
只是为了它,根本没有。死了。
更新二:
尽管 @Import
与标准 spring 测试一起工作:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {Some.class,
Some2WhichDependsOnWorkerTestConfig.class})
@Import(WorkerTestConfig.class)
@ActiveProfiles("test")
public class SomeOtherTest {
未定义 @Import
classes 在测试中使用时的处理顺序。添加用于测试的 @Import
功能主要是为了让 其他 beans 可以轻松注册,无意将其用于 replace bean 定义。
如果你想深入了解到底发生了什么,你可以打开 ConfigurationClassParser
并在 doProcessConfigurationClass
中添加一个条件断点。添加以下条件代码:
System.err.println(configClass);
return false;
现在,如果您调试应用程序,您将在处理配置 classes 时获得附加输出。
当您使用没有 @Import
的 classes
注释属性时,您将看到:
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
,而第二个版本在 WorkerConfig
之前加载 SomeTestSecondConfiguration
。
您还会注意到第二个版本有一个 ImportsContextCustomizer$ImportsConfiguration
class,它会触发额外的导入。
如果您查看 SpringBootTestContextBootstrapper
,您可以在 getOrFindConfigurationClasses
方法中看到定义了顺序,并且您的附加测试 classes 将始终列在主要配置之后。
tl;dr 如果您需要定义顺序,请使用 classes
属性。如果你想添加额外的 beans 并且你不想覆盖任何东西,请使用 @Import
.
您可能还想看看 @MockBean
,它提供了一种用模拟替换 bean 的更可靠的方法。
考虑以下集成测试注释:
@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/set 个 bean,无论何时我使用 @ContextConfiguration
真实的 bean 正在退出并使用来自 WorkerTestConfig 的 bean,无论何时我使用@Import
真正的 bean 仍然被创建并且没有通过测试。
WorkerTestConfig
本身尽可能的琐碎:
@TestConfiguration
public class WorkerTestConfig {
@Primary
@Bean
public ScheduledExecutorService taskExecutor() {
return DirectExecutorFactory.createSameThreadExecutor();
}
}
谁能解释一下 @SpringBootTest 注解的另一个神奇行为?如果您重现相同的行为,请确认,以便我可以转到问题跟踪器,因为我看到有人在 SO 上使用 @Import
和 @SpringBootTest
,并且在 spring 引导文档中没有禁止它:
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有 additive/overriding 行为, import 什么都不做。
注释的完全限定导入是:
导入 org.springframework.context.annotation.Import;
也试图从 @TestConfiguration
更改为 @Configuration
只是为了它,根本没有。死了。
更新二:
尽管 @Import
与标准 spring 测试一起工作:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {Some.class,
Some2WhichDependsOnWorkerTestConfig.class})
@Import(WorkerTestConfig.class)
@ActiveProfiles("test")
public class SomeOtherTest {
未定义 @Import
classes 在测试中使用时的处理顺序。添加用于测试的 @Import
功能主要是为了让 其他 beans 可以轻松注册,无意将其用于 replace bean 定义。
如果你想深入了解到底发生了什么,你可以打开 ConfigurationClassParser
并在 doProcessConfigurationClass
中添加一个条件断点。添加以下条件代码:
System.err.println(configClass);
return false;
现在,如果您调试应用程序,您将在处理配置 classes 时获得附加输出。
当您使用没有 @Import
的 classes
注释属性时,您将看到:
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
,而第二个版本在 WorkerConfig
之前加载 SomeTestSecondConfiguration
。
您还会注意到第二个版本有一个 ImportsContextCustomizer$ImportsConfiguration
class,它会触发额外的导入。
如果您查看 SpringBootTestContextBootstrapper
,您可以在 getOrFindConfigurationClasses
方法中看到定义了顺序,并且您的附加测试 classes 将始终列在主要配置之后。
tl;dr 如果您需要定义顺序,请使用 classes
属性。如果你想添加额外的 beans 并且你不想覆盖任何东西,请使用 @Import
.
您可能还想看看 @MockBean
,它提供了一种用模拟替换 bean 的更可靠的方法。