无法在Spring Boot测试中覆盖Bean。

huangapple go评论49阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2023年7月4日 21:39:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76613235.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定