Search code examples
springspring-bootspring-boot-test

BeanDefinitionOverrideException when supplying bean for integration test / @SpringBootTest


I configure a Clock bean like this:

@Configuration
public class ClockConfiguration {

    @Bean
    Clock clock() {
        return Clock.systemDefaultZone();
    }
}

For an integration test, however, I need a Clock instance with a fixed time, so I added a static @TestConfiguration like this:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(initializers = WireMockInitializer.class)
@AutoConfigureWebTestClient
@ActiveProfiles("test")
class MyIT {

    @TestConfiguration
    static class ClockTestConfiguration {

        @Bean
        public Clock clock() {
            return Clock.fixed(Instant.ofEpochMilli(1635513004000L), ZoneId.systemDefault());
        }
    }

    @Autowired
    WireMockServer wireMockServer;

    @Autowired
    private WebTestClient webTestClient;

    @AfterEach
    void afterEach() {
        wireMockServer.resetAll();
    }

    @Test
    void testFoo() {}
}

But when running the test, the application context cannot be loaded. Instead, this error message is shown:

org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'clock' defined in de.myapp.MyIT$ClockTestConfiguration

There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=clockConfiguration; factoryMethodName=clock; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [de/myapp/ClockConfiguration.class]] bound.

My understanding was that the static @TestConfiguration would actually take care of that?


Solution

  • From Spring boot 2.0 and upwards, you have to enable bean overriding in application.yml to allow Spring to override the instance of Clock from the actual application with the one in you want in integration test:

    spring:
      main:
        allow-bean-definition-overriding: true