0

What I am trying to achieve:

Run an integration test for a Spring Controller in a test container, use a TestJpaConfig instead of JpaConfig:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
@Testcontainers
@ComponentScan(excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = JpaConfig.class)
})
@Import(TestJpaConfig.class)
public class SomeControllerIT {

    @Container
    static PostgreSQLContainer<?> postgresContainer = new PostgreSQLContainer...

    @DynamicPropertySource
    static void overrideProperties(DynamicPropertyRegistry registry) {
        ...
    }

    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate restTemplate;

    @MockBean
    Bean beans...

    @Autowired
    Class classes...

    @BeforeEach
    void setup() {
        ...
    }

    @Test
    void test_Something() throws Exception {
        ...
    }
}

I am having this error:

Caused by: org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'jpaAuditingHandler' defined in null: Cannot register bean definition [Root bean: class [org.springframework.data.auditing.AuditingHandler]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=from; initMethodNames=null; destroyMethodNames=null] for bean 'jpaAuditingHandler' since there is already [Root bean: class [org.springframework.data.auditing.AuditingHandler]; scope=; abstract=false; lazyInit=null; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=from; initMethodNames=null; destroyMethodNames=null] bound.

My JpaConfig:

@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class JpaConfig {
    @Bean
    public AuditorAware<Long> auditorProvider() {
        return new SpringSecurityAuditorAware();
    }
}

And my TestJpaConfig:

@TestConfiguration
@EnableJpaAuditing(auditorAwareRef = "testAuditorProvider")
public class TestJpaConfig {

    @Bean(name = "test")
    public AuditorAware<Long> auditorProvider() {
        return () -> Optional.of(1L);
    }
}

I've tried to work to fix this error without success, asking here for help now, any ideas? I have limited knowledge in Spring.

1 Answer 1

1

The jpaAuditingHandler bean is automatically registered by Spring Data JPA when you use the @EnableJpaAuditing annotation. It is not explicitly defined in your code but is created internally by Spring Data JPA to handle auditing functionality.

When you annotate a configuration class with @EnableJpaAuditing, Spring Data JPA registers the AuditingHandler bean (with the name jpaAuditingHandler) to manage auditing metadata, such as created and modified dates or the user responsible for changes.

In your case, both JpaConfig and TestJpaConfig are annotated with @EnableJpaAuditing, which causes Spring to attempt to register two jpaAuditingHandler beans, leading to the BeanDefinitionOverrideException.

I think below solution may work for you.

  1. Ensure that JpaConfig is excluded from the test context. You are already using @ComponentScan with an excludeFilters, but JpaConfig might still be picked up by Spring Boot's auto-configuration. To ensure it is excluded, explicitly exclude it using @SpringBootTest:
@SpringBootTest(
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
    classes = {SomeControllerIT.class, TestJpaConfig.class} // Explicitly include TestJpaConfig
)
@ActiveProfiles("test")
@Testcontainers
@ComponentScan(excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = JpaConfig.class)
})
public class SomeControllerIT {
    ...
}
  1. If you want to allow bean overriding (not recommended for production but may be acceptable for tests), you can enable it in your application-test.properties or programmatically:

spring.main.allow-bean-definition-overriding=true

Alternatively, you can set it programmatically in your test class:

@SpringBootTest(
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
    properties = "spring.main.allow-bean-definition-overriding=true"
)

Found this SO answer which provides an alternative way to handle this error using @ConditionalOnMissingBean. See if it suits your need.


Update: For missing ServletWebServerFactory bean error,

  1. Ensure spring-boot-starter-web is present in your pom/gradle.
  2. If it is present, you can manually define ServletWebServerFactory bean in your test configuration.
@Configuration
public class TestWebServerConfig {
    @Bean
    public ServletWebServerFactory servletWebServerFactory() {
        return new TomcatServletWebServerFactory();
    }
}

Then include this configuration in your test:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration(classes = {
        SomeAuthIT.class, TestJpaConfig.class, TestWebServerConfig.class
})
@ActiveProfiles("test")
@Testcontainers
@ComponentScan(excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = JpaConfig.class)
})
public class YourTestClass {
    // Test methods
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for your input! This does however throw No qualifying bean of type 'org.springframework.boot.web.servlet.server.ServletWebServerFactory' available: Unable to start AnnotationConfigServletWebServerApplicationContext due to missing ServletWebServerFactory bean because it can't find and start @SpringBootApplication ?
@CJR Yes, that's first thing I would check. Verify that your main application class is annotated with @SpringBootApplication. Every Spring Boot application should have a main class annotated with @SpringBootApplication. Also, ensure your pom.xml includes the spring-boot dependency
Yes it does of course, but when I explicity add SomeAuthIT & TestJpaConfig to SpringBootTest classes, the error above is thrown?
It looks like spring is not able to find the class during test. Updated the answer with some pointers which can help.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.