0

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?

Walking Corpse
  • 107
  • 7
  • 31
  • 49
  • [it can help you](https://stackoverflow.com/questions/54562759/how-to-override-spring-bean-in-integration-test-with-custom-bean-definition) – Andrei Lisa Jul 04 '23 at 13:55

1 Answers1

1

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.
Ken Chan
  • 84,777
  • 26
  • 143
  • 172