英文:
Unable to override Bean in spring boot test
问题
在我的Spring Boot应用程序中,我有一个指定的Bean:
package com.kiteautotrade.autotrader;
@SpringBootApplication
@Configuration
@EnableAsync
@ComponentScan(basePackages = {"com.gmailclient", "com.kiteautotrade.autotrader"})
public class AutotraderApplication implements CommandLineRunner {
// ...
@Bean
public Clock getClock() {
return Clock.system(ZoneId.of("Asia/Kolkata"));
}
}
在Spring Boot测试中,我尝试提供一个不同的Bean:
package com.kiteautotrade.autotrader.integration;
// ...
@ExtendWith(SpringExtension.class)
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AutotraderApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntegrationTest {
@Configuration
@ComponentScan(basePackages = {"com.gmailclient", "com.kiteautotrade.autotrader"})
static class MyTestConfiguration {
@Bean
@Primary
public Clock getClock() {
return Clock.fixed(
LocalDateTime.of(2023, 4, 25, 9, 30)
.toInstant(ZoneOffset.ofHoursMinutes(5, 30)), ZONE_ID);
}
}
}
然而,这个Bean没有被覆盖。我做错了什么?我还尝试在上面的静态类MyTestConfiguration中添加@TestConfiguration,而不是或者附加@Configuration,但没有任何区别。
看起来应用程序类的@Configuration首先生效,因为它的包含在测试的组件扫描中,但我需要为测试覆盖Bean。我做错了什么?
(注意:我没有回答你是否要翻译的问题,只提供了翻译好的代码部分。)
英文:
In my spring boot application I have a bean specified:
package com.kiteautotrade.autotrader;
@SpringBootApplication
@Configuration
@EnableAsync
@ComponentScan(basePackages = {"com.gmailclient", "com.kiteautotrade.autotrader"})
public class AutotraderApplication implements CommandLineRunner {
. . .
@Bean
public Clock getClock() {
return Clock.system(ZoneId.of("Asia/Kolkata"));
}
}
In the spring boot test I try to provide a different bean:
package com.kiteautotrade.autotrader.integration;
. . .
@ExtendWith(SpringExtension.class)
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AutotraderApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntegrationTest {
@Configuration
@ComponentScan(basePackages = {"com.gmailclient", "com.kiteautotrade.autotrader"})
static class MyTestConfiguration {
@Bean
@Primary
public Clock getClock() {
return Clock.fixed(
LocalDateTime.of(2023, 4, 25, 9, 30)
.toInstant(ZoneOffset.ofHoursMinutes(5, 30)), ZONE_ID);
}
}
However the bean is not getting overridden. What am I doing wrong? I have also treid adding @TestConiguration instead of / in addition to @Configuration in the static class MyTestConfiguration above, but it didn't make any difference.
It appears like the application class's @Configuration takes effect first since its package is in the component scan of the test, but I need to override the Bean with a different one for the test. What am I doing wrong?
答案1
得分: 1
嵌套的 @Configuration
仅在您未明确配置加载 Spring 上下文的类到 @SpringBootTest
中时才会生效。
因此,在 @SpringBootTest
中也必须明确指定它(通过 classes
属性)才能生效。
然后,在 Spring 上下文启动时,您将遇到 BeanDefinitionOverrideException
,因为它会抱怨您不能定义具有相同名称的多个bean。您必须配置 spring.main.allow-bean-definition-overriding=true
来放宽这种限制,允许稍后处理的bean覆盖先前处理的具有相同名称的bean。
bean 的处理顺序基于在 @SpringBootTest
中指定的上下文类的顺序。
因此,将以下更改组合起来应该可以解决您的问题:
@SpringBootTest(
classes = {AutotraderApplication.class, IntegrationTest.MyTestConfiguration.class},
properties = {"spring.main.allow-bean-definition-overriding=true"},
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntegrationTest {
@Configuration
@ComponentScan(basePackages = {"com.gmailclient", "com.kiteautotrade.autotrader"})
static class MyTestConfiguration {
@Bean
public Clock getClock() {
return Clock.fixed(
LocalDateTime.of(2023, 4, 25, 9, 30)
.toInstant(ZoneOffset.ofHoursMinutes(5, 30)), ZONE_ID);
}
}
}
注意:
- 似乎在测试Clock bean中的
@Primary
是不必要的。 - 可以删除
@ExtendWith(SpringExtension.class)
,因为@SpringBootTest
已经包含了它。 @RunWith(SpringRunner.class)
也是不必要的。它是为 JUnit4 设计的。
英文:
The nested @Configuration
will only be considered if you do not explicitly configure the classes to load the spring context in @SpringBootTest
.
So , you have to also explicitly specify it in @SpringBootTest
(via classes
property) to make it take effect.
Then you will encounter BeanDefinitionOverrideException
when spring context startup as it complains you cannot define multiple beans with the same name. You have to configure spring.main.allow-bean-definition-overriding=true
to relax such restriction which allows the bean that is processed later to override bean that is processed previously if they have the same name.
The bean processing order are based on the order of the context classes that are specified in @SpringBootTest
.
So combining these means changing to the following should fix your issue :
@SpringBootTest(
classes = {AutotraderApplication.class, IntegrationTest.MyTestConfiguration.class},
properties = {"spring.main.allow-bean-definition-overriding=true"},
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntegrationTest {
@Configuration
@ComponentScan(basePackages = {"com.gmailclient", "com.kiteautotrade.autotrader"})
static class MyTestConfiguration {
@Bean
public Clock getClock() {
return Clock.fixed(
LocalDateTime.of(2023, 4, 25, 9, 30)
.toInstant(ZoneOffset.ofHoursMinutes(5, 30)), ZONE_ID);
}
}
}
Notes :
- Seems that
@Primary
in the test Clock bean is unnecessary - Can remove
@ExtendWith(SpringExtension.class)
as@SpringBootTest
already includes it. @RunWith(SpringRunner.class)
is also unnecessary. It is for JUnit4.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论